Skip to content

Commit

Permalink
Merge pull request #37 from mozilla-services/wstuckey/glean-plugin
Browse files Browse the repository at this point in the history
feat(plugin): Add a glean plugin for tracking usage
  • Loading branch information
quiiver authored Oct 31, 2024
2 parents 1eef6c5 + 94590ce commit c190e05
Show file tree
Hide file tree
Showing 15 changed files with 367 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ site

# E2E test reports
e2e-test-report/

.venv
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ COPY package.json yarn.lock .yarnrc.yml ./
COPY .yarn ./.yarn

COPY packages packages

# Comment this out if you don't have any internal plugins
#COPY plugins plugins
COPY plugins plugins

RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -exec rm -rf {} \+

Expand Down
5 changes: 5 additions & 0 deletions app-config.production.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
app:
# Should be the same as backend.baseUrl when using the `app-backend` plugin.
baseUrl: https://${BASE_URL}
analytics:
glean:
appId: moz_backstage
enabled: true
environment: production

backend:
# Note that the baseUrl should be the URL that the browser and other clients
Expand Down
8 changes: 8 additions & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
app:
title: Moz Backstage App
baseUrl: http://localhost:3000
analytics:
glean:
appId: moz_backstage
enabled: true
debug:
logging: false
tag: backstage-dev
environment: development

organization:
name: Mozilla
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"engines": {
"node": "18 || 20"
},
"repository": {
"url": "git+https://github.com/mozilla-services/moz-backstage-app.git",
"type": "git"
},
"scripts": {
"dev": "yarn workspaces foreach -A --include backend --include app --parallel -v -i run start",
"start": "yarn workspace app start",
Expand Down
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@backstage/theme": "^0.6.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"backstage-plugin-glean": "^0.1.0",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router": "^6.3.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ import {
scmIntegrationsApiRef,
ScmAuth,
} from '@backstage/integration-react';

import {
AnyApiFactory,
configApiRef,
createApiFactory,
} from '@backstage/core-plugin-api';

import { createGleanApiFactory } from 'backstage-plugin-glean';

export const apis: AnyApiFactory[] = [
createApiFactory({
api: scmIntegrationsApiRef,
deps: { configApi: configApiRef },
factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
}),
ScmAuth.createDefaultApiFactory(),
createGleanApiFactory(),
];
1 change: 1 addition & 0 deletions plugins/glean/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
19 changes: 19 additions & 0 deletions plugins/glean/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# glean

Welcome to the glean plugin!

_This plugin was created through the Backstage CLI_

## Add API to `packages/app/src/apis.ts`

```js
import { createGleanAPIFactory } from 'backstage-plugin-glean';

export const apis: AnyApiFactory[] = [
//...
createGleanAPIFactory(),
];
```

## Updating Metrics
After adding new metrics to `metrics.yaml` run `npm build:glean` and commit the updating files in `src/metrics`
52 changes: 52 additions & 0 deletions plugins/glean/config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export interface Config {
app: {
analytics?: {
glean?: {
/**
* Glean App ID
* @visibility frontend
*/
appId: string;

/**
* Debugging configuration
*
* @visibility frontend
*/
debug?: {
/**
* Log analytics debug statements to the console.
* Defaults to false.
*
* @visibility frontend
*/
logging?: boolean,

/**
* Set a debug tag for use in the Glean Debug Viewer.
* Defaults to undefined.
*
* @visibility frontend
*/
tag?: string,
}

/**
* Whether to enable Glean Analytics.
* Defaults to false.
*
* @visibility frontend
*/
enabled?: boolean;

/**
* Maps to glean channel. Required
*
* @visibility frontend
*/
environment: 'development' | 'production';

};
};
};
}
26 changes: 26 additions & 0 deletions plugins/glean/metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0

backstage:
create:
type: event
description: |
Create software from template.
extra_keys:
name:
description: Name of software being created
type: string
entity_ref:
description: template ref
type: string
time_saved:
description: time saved by running the template
type: quantity
notification_emails:
- [email protected]
bugs:
- https://bugzilla.mozilla.org/00000
data_reviews:
- https://bugzilla.mozilla.org/00000#
expires: never

53 changes: 53 additions & 0 deletions plugins/glean/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "backstage-plugin-glean",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"sideEffects": false,
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"build:glean": "glean translate metrics.yaml -f typescript -o src/metrics",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.15.1",
"@backstage/core-plugin-api": "^1.10.0",
"@mozilla/glean": "^5.0.3",
"react-router-dom": "^6.27.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.28.0",
"@backstage/core-app-api": "^1.15.1",
"@backstage/dev-utils": "^1.1.2",
"@backstage/test-utils": "^1.7.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"msw": "^1.0.0",
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
},
"files": [
"dist",
"config.d.ts"
],
"configSchema": "config.d.ts"
}
92 changes: 92 additions & 0 deletions plugins/glean/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
AnalyticsApi,
AnalyticsEvent,
AnyApiFactory,
ConfigApi,
analyticsApiRef,
configApiRef,
createApiFactory,
} from '@backstage/core-plugin-api';

import Glean from '@mozilla/glean/web';
import GleanMetrics from '@mozilla/glean/metrics';
import { ConfigurationInterface as GleanConfig } from '@mozilla/glean/dist/types/core/config';
import { JsonObject } from '@backstage/types';
import { create } from './metrics/backstage';

interface DebugConfig extends JsonObject {
logging?: boolean;
tag?: string;
}

export class GleanAnalytics implements AnalyticsApi {
private constructor(
appId: string,
enabled: boolean,
gleanConfig: GleanConfig,
debug?: DebugConfig,
) {
if (debug) {
Glean.setLogPings(!!debug.logging);
// set the debug tag if it is defined
debug.tag && Glean.setDebugViewTag(debug.tag);
}
Glean.initialize(appId, enabled, gleanConfig);
}

static fromConfig(config: ConfigApi): GleanAnalytics {
const appId = config.getString('app.analytics.glean.appId');
const enabled = config.getBoolean('app.analytics.glean.enabled');
const debug = config.getOptional(
'app.analytics.glean.debug',
) as DebugConfig;
const environment = config.getString('app.analytics.glean.environment');

return new GleanAnalytics(
appId,
enabled,
{
enableAutoPageLoadEvents: false,
enableAutoElementClickEvents: false,
channel: environment,
},
debug,
);
}

captureEvent(event: AnalyticsEvent) {
const { action, subject } = event;
switch (action) {
case 'navigate':
GleanMetrics.pageLoad({
title: subject,
url: window.location.toString(),
referrer: document.referrer,
});
break;
case 'click':
GleanMetrics.recordElementClick({
id: event.attributes?.to.toString(),
label: subject,
});
break;
case 'create':
create.record({
name: subject,
entity_ref: event.attributes?.entityRef.toString(),
time_saved: event.value,
});
break;

default:
break;
}
}
}

export const createGleanApiFactory: () => AnyApiFactory = () =>
createApiFactory({
api: analyticsApiRef,
deps: { configApi: configApiRef },
factory: ({ configApi }) => GleanAnalytics.fromConfig(configApi),
});
26 changes: 26 additions & 0 deletions plugins/glean/src/metrics/backstage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// AUTOGENERATED BY glean_parser v14.5.2. DO NOT EDIT. DO NOT COMMIT.

import EventMetricType from "@mozilla/glean/private/metrics/event";

/**
* Create software from template.
*
* Generated from `backstage.create`.
*/
export const create = new EventMetricType<{
entity_ref?: string,
name?: string,
time_saved?: number,
}>({
category: "backstage",
name: "create",
sendInPings: ["events"],
lifetime: "ping",
disabled: false,
}, ["entity_ref", "name", "time_saved"]);


Loading

0 comments on commit c190e05

Please sign in to comment.