Events and metrics add useful data to Compass components. Events populate the activity feed, and metrics are displayed right on the component page. Hooking into Compass events and metrics gives your app powerful extension capabilities out of the box that are baked into the core Compass experience. To do this, you can create a Forge app with the compass:dataProvider module.
To get started with a template that includes boilerplate and instructions for an app that ingests events and metrics via dataProvider, fork this example repo.
Creating an event or metric in Compass is relatively easy (see Sending in new events and metrics). The trickier part is automatically linking the correct event or metric with the correct Compass component(s).
Compass apps use component links to automatically infer which events and metrics from a particular Forge app should be connected to a component. For example, all a user has to do is add https://bitbucket.org/atlassian/really-cool-repo
to the repository links section on a component to automatically get events and metrics from the really-cool-repo
Bitbucket repository on that component if the Bitbucket app is installed.
To establish your app as a provider of data from external systems, add the compass:dataProvider module to your app manifest. Compass automatically triggers this module whenever a new link is added to a component. You can decide how to respond to this link events. The most common use-case is to initialize and backfill events and metrics from the external source that was linked to the component.
In order for your app to provide events data, add these scopes to your app manifest:
write:component:compass
write:event:compass
In order for your app to provide metrics data, add this scope to your manifest:
write:metric:compass
Declare the compass:dataProvider
module in your app manifest. In the module definition:
linkTypes
is a list of Compass link types that your app recognizes. The possible values are chat-channel
, dashboard
, document
, on-call
, project
, repository
, and other-link
.domains
is a list of domains that your app recognizes. Wildcard domains are supported, for example use "*.bitbucket.org"
to match any subdomain on bitbucket.org
. Wildcard domains must be enclosed within double quotes.function
is the function that gets invoked when a user adds a link whose type matches one of the specified linkTypes
and whose URL matches one of the specified domains
.Here's an example module definition for an app that handles Bitbucket events and metrics:
1 2modules: compass:dataProvider: - key: data-provider function: data-provider-fn linkTypes: - repository domains: - 'bitbucket.org' function: - key: data-provider-fn handler: index.dataProvider
Your data provider's handler function receives one input parameter, the url
of the component link that was just added. It's sent as an object that looks like this:
1 2{ url: string }
Your function should first decide whether it can actually handle this link. Typically this involves checking with your external events/metrics system to see whether the URL exists. If it doesn’t, your function can just return null
at this point, to indicate that no further action is needed.
However, if the URL does match an entity in your external system, then you will specify an external identifier for that entity as the externalSourceId
. This value can be can be set to anything you want, as long as it's unique per entity and won't change over time.
For example, we use Bitbucket repository ID as the externalSourceId
that uniquely identifies Bitbucket repositories.
Your handler function returns a payload that tells Compass:
The payload should contain the externalSourceId
for the URL (if you found one), the events and metrics you’ll be sending in from that external system, and optional initial values for the events and metrics.
To help you construct your return information to send to Compass, we suggest that you use the DataProviderResponse class from the GraphQL API Toolkit.
Below is an example implementation for Bitbucket. It includes some function stubs for Bitbucket-specific logic.
1 2import { DataProviderResponse, DataProviderEventTypes, BuiltinMetricDefinitions, DataProviderResult, } from '@atlassian/forge-graphql' async function dataProvider(request: { url: string }): Promise<DataProviderResult | null> { const { url } = request; const externalSourceId = getExternalSourceIdFromUrlStub(url); if (!externalSourceId) { // The URL doesn't represent a repo in the connected Bitbucket workspace. // In this case, just return `null` to show that you don't care about the link. return null; } // Initialize the `DataProviderResponse` class with desired events & metrics to enable const response = new DataProviderResponse(externalSourceId, { eventTypes: [DataProviderEventTypes.DEPLOYMENTS, DataProviderEventTypes.BUILDS], builtInMetricDefinitions: [{ name: BuiltinMetricDefinitions.BUILD_SUCCESS_RATE, derived: true}], customMetricDefinitions: [{ name: 'My custom metric', format: { suffix: 'req/min' } }], }); // Retrieve events from the external system & calculate metrics const deploymentValues = await getDeploymentValuesFromBitbucketStub(externalSourceId); const buildValues = await getBuildValuesFromBitbucketStub(externalSourceId); const buildSuccessRate = calculateBuildSuccessRateStub(buildValues); const customMetric = 33; // Add initial values & return the response return response .addDeployments(deploymentValues) .addBuilds(buildValues) .addBuiltInMetricValue(BuiltinMetricDefinitions.BUILD_SUCCESS_RATE, buildSuccessRate) .addCustomMetricValue('My custom metric', customMetric) .build(); }
Optionally, your app can provide initial values for all events and metrics that you set up. This lets users see events and metrics in Compass immediately after adding a link to a component, rather than having to wait until the next event or metric update to see data. While this is an optional step, it’s recommended to provide a better user experience.
Regardless of whether you set initial values, the constructor of the DataProviderResponse
class must specify all event and metric types you plan on providing. Compass uses this information to set up the event and metric connections on components, which must happen before the component can receive events or metrics.
Compass has a set of predefined metrics that should be used when possible. Predefined metrics can’t be deleted from the UI by end users and represent common component health metrics important to development teams. See the Forge GraphQL Toolkit reference for a list of predefined metric identifiers.
You can also create a custom metric if the metric you want to add isn’t present in the list of pre-defined metrics. Custom metrics must have a name
which is a unique identifier for that metric. You can optionally provide a description and custom suffix as well.
Some predefined metrics can be designated as derived meaning that Compass will automatically calculate them based on event data without your app manually inserting metric value updates.
Using derived metrics removes the burden of building bespoke calculation logic from your app and standardizes calculations across apps.
Compass reads the return value of your function and attempts to set up events and metrics for the component where you add a link. However, as with all things in software, this has the potential to fail.
Add the callback
property to the module definition to catch any failures that might occur.
1 2modules: compass:dataProvider: - key: data-provider function: data-provider-fn callback: function: data-provider-callback-fn linkTypes: - repository domains: - 'bitbucket.org' function: - key: data-provider-fn handler: index.dataProvider - key: data-provider-callback-fn handler: index.dataProviderCallback
This enables you to respond to errors, such as logging what went wrong to help you debug your code. The most likely errors are due to insufficient scopes in your manifest or an invalid return payload, but if the error happens to be an internal Compass issue please reach out in the Compass community.
The input object provided as the first parameter to your callback function will look like this:
1 2{ success: boolean, // if the request was successful url: string, // the URL the request was performed on errorMessage: string | undefined // what went wrong, if an error did occur }
The dataProvider
module takes care of some important processes: setting up events and metrics for ingestion, linking them to the correct components, and initializing them with data. However, after this is done you actually need to send in the event and metric updates as new events occur and as metrics change over time.
The process for this will vary with your external system, but generally, you’ll want to listen to events in the external system (i.e. through webhooks) and send them into Compass using the createEvent method from the Forge GraphQL Toolkit. You should create events using the same externalSourceId
that you used in the dataProvider
module so that Compass can link the events to the correct components. If your metrics are based on event data, this is a great time to recalculate your metrics as well and send them to Compass. Otherwise, you can calculate your metrics on a cadence using scheduled triggers or devise your own means of updating cadence.
To insert metrics, you should use the insertMetricValueByExternalId method from the Forge GraphQL Toolkit, and once again use the same externalSourceId
s.
To find metricDefinitionId
for predefined metrics, refer to the list of predefined metrics. To find metricDefinitionId
for custom metrics, use the GetMetricDefinitions query to search for your newly created metric definition by name, and then use the id
field on the response object of the matching definition.
At the time your Forge app is installed and configured, there might already be links to external systems your app cares about that have been added to Compass components. Since the data provider module only gets triggered when new links get added, components that already have those links will never get associated with events and metrics from that external system.
To address this, your app should use the synchronizeLinkAssociations method from the Forge GraphQL Toolkit. For example:
1 2await graphqlGateway .compass .asApp() .synchronizeLinkAssociations({ cloudId: cloudId, forgeAppId: forgeAppId, });
This iterates through all links currently on the Compass site your app is installed on, calls the dataProvider
module for each of them (if they match the filter parameters in your manifest), and sets up the resulting event and metric connections.
This operation should be run every single time your app is first configured or is connected to a new account in the external system it integrates with. To explain why, let’s say a user disconnects their Bitbucket workspace and connects to a different one. This workspace will have a different set of repositories than the previously connected workspace, so the Bitbucket app will be able to fetch events and metrics from an entirely different set of links.
This mutation can also be used anytime you want to modify your data provider implementation to add support for a new event or metric. We suggest triggering it for all users using a scheduled trigger, but you can call it in whatever way makes the most sense for your use case.
This operation is idempotent, so feel empowered to re-run it when needed.
Compass automatically handles link deletions and will remove any associated events or metrics that were added to the component based on that link. We store the association internally and won’t need to call the data provider module in your Forge app.
If a link was updated but the URL wasn’t changed, then no action is needed. If the link was updated and the URL was changed, then it will remove the events and metrics associated with the component from the old link URL and re-query your data provider module to get a new set of event and metric information.
If a link remains on a component, but that link was deleted in the external system, you’d have to manually delete any associated event and metric sources with your Forge app. You could do this through the use of the GraphQL SDK, or simply decide to leave them on components as a historical record.
The data provider module does not currently support the deletion of the event or metric data if an empty response is returned for a link that has active data associated with it.
Rate this page: