merged in master

Signed-off-by: Daniel Deloff <44780793+rv-ddeloff@users.noreply.github.com>
This commit is contained in:
Daniel Deloff
2021-10-12 11:53:56 -04:00
266 changed files with 2878 additions and 1430 deletions
+46
View File
@@ -0,0 +1,46 @@
---
id: how-to-guides
title: Search "HOW TO" guides
sidebar_label: "HOW TO" guides
description: Search "HOW TO" guides
---
## How to implement your own Search API
The Search plugin provides implementation of one primary API by default: the
[SearchApi](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L35),
which is responsible for talking to the search-backend to query search results.
There may be occasions where you need to implement this API yourself, to
customize it to your own needs - for example if you have your own search backend
that you want to talk to. The purpose of this guide is to walk you through how
to do that in two steps.
1. Implement the `SearchApi`
[interface](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L31)
according to your needs.
```typescript
export class SearchClient implements SearchApi {
// your implementation
}
```
2. Override the API ref `searchApiRef` with your new implemented API in the
`App.tsx` using `ApiFactories`.
[Read more about App APIs](https://backstage.io/docs/api/utility-apis#app-apis).
```typescript
const app = createApp({
apis: [
// SearchApi
createApiFactory({
api: searchApiRef,
deps: { discovery: discoveryApiRef },
factory({ discovery }) {
return new SearchClient({ discoveryApi: discovery });
},
}),
],
});
```
+89
View File
@@ -139,3 +139,92 @@ default app configs.
You can learn more about the local configuration in
[Static Configuration in Backstage](../conf/) section.
## Writing changesets
Changesets are an important part of the development process. They are used to
generate Changelog entries for all changes to the project. Ultimately they are
read by the end users to learn about important changes and fixes to the project.
Some of these fixes might require manual intervention from users so it's
important to write changesets that users understand and can take action on.
Here are some important do's and don'ts when writing changesets:
### Changeset should give a clear description to what has changed
#### Bad
```
---
'@backstage/catalog': patch
---
Fixed table layout
```
#### Good
```
---
'@backstage/catalog': patch
---
Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels.
```
### Breaking changes not caught by the type checker should be clearly marked with bold **BREAKING** text
#### Bad
```
---
'@backstage/catalog': minor
---
getEntity is now a function that returns a Promise.
```
#### Good
```
---
'@backstage/catalog': minor
---
**BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on.
```
### Changes to code should include a diff of the files that need updating
#### Bad
```
---
'@backstage/catalog': patch
---
**BREAKING** The catalogEngine now requires a flux capacitor to be passed.
```
#### Good
---
'@backstage/catalog': patch
---
**BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is
passed to the router.
These changes are **required** to `packages/backend/src/plugins/catalog.ts`
```diff
+ import { FluxCapacitor } from '@backstage/time';
+ const fluxCapacitor = new FluxCapacitor();
return await createRouter({
entitiesCatalog,
locationAnalyzer,
locationService,
+ fluxCapacitor,
logger: env.logger,
config: env.config,
});
```
@@ -1,23 +0,0 @@
---
id: installation
title: Google Analytics Installation
sidebar_label: Installation
# prettier-ignore
description: Adding Google Analytics to Your App
---
There is a basic
[Google Analytics](https://marketingplatform.google.com/about/analytics/)
integration built into Backstage. You can enable it by adding the following to
your app configuration:
```yaml
app:
googleAnalyticsTrackingId: UA-000000-0
```
Replace the tracking ID with the one generated for you after signing up for the
Google Analytics service.
The default behavior is only to send a pageview hit. To record more, review the
[Google Analytics developer documentation](https://developers.google.com/analytics/devguides/collection/gtagjs).
+298
View File
@@ -0,0 +1,298 @@
---
id: analytics
title: Plugin Analytics
description: Measuring usage of your Backstage instance.
---
Setting up, maintaining, and iterating on an instance of Backstage can be a
large investment. To help measure return on this investment, Backstage comes
with an event-based Analytics API that grants app integrators the flexibility to
collect and analyze Backstage usage in the analytics tool of their choice, while
providing plugin developers a standard interface for instrumenting key user
interactions.
## Concepts
- **Events** consist of, at a minimum, an `action` (like `click`) and a
`subject` (like `thing that was clicked on`).
- **Attributes** represent additional dimensional data (in the form of key/value
pairs) that may be provided on an event-by-event basis. To continue the above
example, the URL a user clicked to might look like `{ "to": "/a/page" }`.
- **Context** represents the broader context in which an event took place. By
default, information like `pluginId`, `extension`, and `routeRef` are
provided.
This composition of events aims to allow analysis at different levels of detail,
enabling very granular questions (like "what is the most clicked on thing on a
particular route") as well as very high-level questions (like "what is the most
used plugin in my Backstage instance") to be answered.
## Supported Analytics Tools
While all that's needed to consume and forward these events to an analytics tool
is a concrete implementation of [AnalyticsApi][analytics-api-type], common
integrations are packaged and provided as plugins. Find your analytics tool of
choice below.
| Analytics Tool | Support Status |
| ---------------------- | -------------- |
| [Google Analytics][ga] | Yes ✅ |
To suggest an integration, please [open an issue][add-tool] for the analytics
tool your organization uses. Or jump to [Writing Integrations][int-howto] to
learn how to contribute the integration yourself!
[ga]:
https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md
[add-tool]:
https://github.com/backstage/backstage/issues/new?assignees=&labels=plugin&template=plugin_template.md&title=%5BAnalytics+Module%5D+THE+ANALYTICS+TOOL+TO+INTEGRATE
[int-howto]: #writing-integrations
[analytics-api-type]:
https://backstage.io/docs/reference/core-plugin-api.analyticsapi
## Key Events
The following table summarizes events that, depending on the plugins you have
installed, may be captured.
| Action | Provided By | Subject |
| ---------- | -------------- | ----------------------------------------- |
| `navigate` | Backstage Core | The URL of the page that was navigated to |
| `click` | Backstage Core | The text of the link that was clicked on |
If there is an event you'd like to see captured, please [open an
issue][add-event] describing the event you want to see and the questions it
would help you answer. Or jump to [Capturing Events][event-howto] to learn how
to contribute the instrumentation yourself!
_OSS plugin maintainers: feel free to document your events in the table above._
[add-event]:
https://github.com/backstage/backstage/issues/new?assignees=&labels=enhancement&template=feature_template.md&title=[Analytics%20Event]:%20THE+EVENT+TO+CAPTURE
[event-howto]: #capturing-events
## Writing Integrations
Analytics event forwarding is implemented as a Backstage utility API. Just as
you might provide a custom API implementation for errors or SCM Authentication,
you can provide one for analytics.
The provided API need only provide a single method `captureEvent`, which takes
an `AnalyticsEvent` object.
```ts
import {
analyticsApiRef,
AnalyticsEvent,
AnyApiFactory,
createApiFactory,
} from '@backstage/core-plugin-api';
export const apis: AnyApiFactory[] = [
createApiFactory(analyticsApiRef, {
captureEvent: (event: AnalyticsEvent) => {
window._AcmeAnalyticsQ.push(event);
},
}),
];
```
In reality, you would likely want to encapsulate instantiation logic and pull
some details from configuration. A more complete example might look like:
```ts
import {
AnalyticsApi,
analyticsApiRef,
AnalyticsEvent,
AnyApiFactory,
configApiRef,
createApiFactory,
} from '@backstage/core-plugin-api';
import { AcmeAnalytics } from 'acme-analytics';
class AcmeAnalytics implements AnalyticsApi {
private constructor(accountId: number) {
AcmeAnalytics.init(accountId);
}
static fromConfig(config) {
const accountId = config.getString('app.analytics.acme.id');
return new AcmeAnalytics(accountId);
}
captureEvent(event: AnalyticsEvent) {
const { action, ...rest } = event;
AcmeAnalytics.send(action, rest);
}
}
export const apis: AnyApiFactory[] = [
createApiFactory({
api: analyticsApiRef,
deps: { configApi: configApiRef },
factory: ({ configApi }) => AcmeAnalytics.fromConfig(configApi),
}),
];
```
If you are integrating with an analytics service (as opposed to an internal
tool), consider contributing your API implementation as a plugin!
By convention, such packages should be named
`@backstage/analytics-module-[name]`, and any configuration should be keyed
under `app.analytics.[name]`.
## Capturing Events
To instrument an event in a component, start by retrieving an analytics tracker
using the `useAnalytics()` hook provided by `@backstage/core-plugin-api`. The
tracker includes a `captureEvent` method which takes an `action` and a `subject`
as arguments.
```ts
import { useAnalytics } from '@backstage/core-plugin-api';
const analytics = useAnalytics();
analytics.captureEvent('deploy', serviceName);
```
### Providing Extra Attributes
Additional dimensional `attributes` as well as a numeric `value` can be provided
on a third `options` argument if/when relevant for the event:
```ts
analytics.captureEvent('merge', pullRequestName, {
value: pullRequestAgeInMinutes,
attributes: {
org,
repo,
},
});
```
In the above example, an event resembling the following object would be
captured:
```json
{
"action": "merge",
"subject": "Name of Pull Request",
"value": 60,
"attributes": {
"org": "some-org",
"repo": "some-repo"
}
}
```
### Providing Context for Events
The `attributes` option is good for capturing details available to you within
the component that you're instrumenting. For capturing metadata only available
further up the react tree, or to help app integrators aggregate distinct events
by some common value, use an `<AnalyticsContext>`.
```tsx
import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api';
const MyComponent = ({ value }) => {
const analytics = useAnalytics();
const handleClick = () => analytics.captureEvent('check', value);
return <SomeThing value={value} onClick={handleClick} />;
};
const MyWrapper = () => {
return (
<AnalyticsContext attributes={{ segment: 'xyz' }}>
<MyComponent value={'Some Value'} />
</AnalyticsContext>
);
};
```
In the above example, clicking on `<SomeThing />` would result in an analytics
event resembling:
```json
{
"action": "check",
"subject": "Some Value",
"context": {
"segment": "xyz"
}
}
```
Note that, for brevity in the example above, the context keys provided by
Backstage core (`pluginId`, `extension`, and `routeRef`) have been omitted. In
reality, those details would be included alongside any additional context
provided by you.
Analytics contexts can be nested; their values are merged down the react tree,
allowing keys to be overwritten.
### Event Naming Considerations
An event is split into its constituent parts to enable analysis at various
levels of granularity. In order to maintain this flexibility at analysis-time,
it's important to keep each of these levels of detail disaggregated.
- Avoid providing an overly specific `action`. For example, instead of
`filterEntityTable`, consider just using `filter` as the action, and allowing
`EntityTable` to be specified as part of the event's `context` (most likely
automatically as part of the `extension` in which the `filter` event was
captured).
- On the flip side, when adding `attributes` to an event, look at existing
events and see if the data you are capturing matches the intention, type, or
even the content of _their_ `attributes`. For instance, it may be common for
events that involve the Catalog to add details like entity `name`, `kind`,
and/or `namespace` as `attributes`. Using the same keys in your event will
ensure that events instrumented across plugins can easily be aggregated.
### Unit Testing Event Capture
The `@backstage/test-utils` package includes a `MockAnalyticsApi` implementation
that you can use in your unit tests to spy on and make assertions about any
analytics events captured.
Use it like this:
```tsx
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
import { analyticsApiRef } from '@backstage/core-plugin-api';
import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils';
import { render, fireEvent, waitFor } from '@testing-library/react';
describe('SomeComponent', () => {
it('should capture event on click', () => {
// Use the Mock Analytics API to spy on event captures.
const apiSpy = new MockAnalyticsApi();
// Render the component being tested
const { getByText } = render(
wrapInTestApp(
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, apiSpy]])}>
<SomeComponentUnderTest />
</ApiProvider>,
),
);
// Fire the event that triggers event capture.
fireEvent.click(getByText('some component text'));
// Assert that the event was captured with the expected data.
await waitFor(() => {
expect(apiSpy.getEvents()[0]).toMatchObject({
action: 'expected action',
subject: 'expected subject'',
attributes: {
foo: 'bar',
},
});
});
});
});
```
+2 -2
View File
@@ -60,7 +60,7 @@ The YAML file must include the following information. Please note that the
indentation for the `privateKey` is required.
```yaml
appId: 1
appId: app id
clientId: client id
clientSecret: client secret
webhookSecret: webhook secret
@@ -95,7 +95,7 @@ If you want to limit the GitHub app installations visible to backstage you may
optionally include the `allowedInstallationOwners` option.
```yaml
appId: 1
appId: app id
allowedInstallationOwners: ['GlobexCorp']
clientId: client id
clientSecret: client secret
-5
View File
@@ -8,11 +8,6 @@ description: Adding Observability to Your Plugin
This article briefly describes the observability options that are available to a
Backstage integrator.
## Google Analytics
See how to install Google Analytics in your app
[here](../integrations/google-analytics/installation.md)
## Datadog RUM Events
See how to install Datadog Events in your app