Introduce an entityRef context key
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Both `EntityProvider` and `AsyncEntityProvider` contexts now wrap all children with an `AnalyticsContext` containing the corresponding `entityRef`; this opens up the possibility for all events underneath these contexts to be associated with and aggregated by the corresponding entity.
|
||||
@@ -301,11 +301,11 @@ it's important to keep each of these levels of detail disaggregated.
|
||||
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
|
||||
- On the flip side, when adding `attributes` to or `context` around 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` or `context`.
|
||||
For instance, it's common for events that involve the Catalog to include an
|
||||
`entityRef` contextual key. Using the same keys and values in your event will
|
||||
ensure that events instrumented across plugins can easily be aggregated.
|
||||
|
||||
### Unit Testing Event Capture
|
||||
|
||||
@@ -23,6 +23,11 @@ import {
|
||||
AsyncEntityProvider,
|
||||
} from './useEntity';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { MockAnalyticsApi, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
|
||||
const entity = { metadata: { name: 'my-entity' }, kind: 'MyKind' } as Entity;
|
||||
|
||||
describe('useEntity', () => {
|
||||
it('should throw if no entity is provided', async () => {
|
||||
@@ -34,7 +39,6 @@ describe('useEntity', () => {
|
||||
});
|
||||
|
||||
it('should provide an entity', async () => {
|
||||
const entity = { kind: 'MyEntity' } as Entity;
|
||||
const { result } = renderHook(() => useEntity(), {
|
||||
wrapper: ({ children }) => (
|
||||
<EntityProvider entity={entity} children={children} />
|
||||
@@ -43,6 +47,24 @@ describe('useEntity', () => {
|
||||
|
||||
expect(result.current.entity).toBe(entity);
|
||||
});
|
||||
|
||||
it('should provide entityRef analytics context', () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const apis = TestApiRegistry.from([analyticsApiRef, analyticsSpy]);
|
||||
const { result } = renderHook(() => useAnalytics(), {
|
||||
wrapper: ({ children }) => (
|
||||
<ApiProvider apis={apis}>
|
||||
<EntityProvider entity={entity} children={children} />
|
||||
</ApiProvider>
|
||||
),
|
||||
});
|
||||
|
||||
result.current.captureEvent('test', 'value');
|
||||
|
||||
expect(analyticsSpy.getEvents()[0]).toMatchObject({
|
||||
context: { entityRef: 'mykind:default/my-entity' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useAsyncEntity', () => {
|
||||
@@ -60,7 +82,6 @@ describe('useAsyncEntity', () => {
|
||||
});
|
||||
|
||||
it('should provide an entity', async () => {
|
||||
const entity = { kind: 'MyEntity' } as Entity;
|
||||
const refresh = () => {};
|
||||
const { result } = renderHook(() => useAsyncEntity(), {
|
||||
wrapper: ({ children }) => (
|
||||
@@ -96,4 +117,43 @@ describe('useAsyncEntity', () => {
|
||||
expect(result.current.error).toBe(error);
|
||||
expect(result.current.refresh).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should provide entityRef analytics context', () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const apis = TestApiRegistry.from([analyticsApiRef, analyticsSpy]);
|
||||
const { result } = renderHook(() => useAnalytics(), {
|
||||
wrapper: ({ children }) => (
|
||||
<ApiProvider apis={apis}>
|
||||
<AsyncEntityProvider
|
||||
loading={false}
|
||||
entity={entity}
|
||||
refresh={() => {}}
|
||||
children={children}
|
||||
/>
|
||||
</ApiProvider>
|
||||
),
|
||||
});
|
||||
|
||||
result.current.captureEvent('test', 'value');
|
||||
|
||||
expect(analyticsSpy.getEvents()[0]).toMatchObject({
|
||||
context: { entityRef: 'mykind:default/my-entity' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should omit entityRef analytics context', () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const apis = TestApiRegistry.from([analyticsApiRef, analyticsSpy]);
|
||||
const { result } = renderHook(() => useAnalytics(), {
|
||||
wrapper: ({ children }) => (
|
||||
<ApiProvider apis={apis}>
|
||||
<AsyncEntityProvider loading={false} children={children} />
|
||||
</ApiProvider>
|
||||
),
|
||||
});
|
||||
|
||||
result.current.captureEvent('test', 'value');
|
||||
|
||||
expect(analyticsSpy.getEvents()[0].context).not.toHaveProperty('entityRef');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { AnalyticsContext } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
@@ -66,7 +67,13 @@ export const AsyncEntityProvider = ({
|
||||
// consumers might be doing things like `useContext(EntityContext)`
|
||||
return (
|
||||
<NewEntityContext.Provider value={createVersionedValueMap({ 1: value })}>
|
||||
{children}
|
||||
<AnalyticsContext
|
||||
attributes={{
|
||||
...(entity ? { entityRef: stringifyEntityRef(entity) } : undefined),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AnalyticsContext>
|
||||
</NewEntityContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user