feat: add backward compatibility for core anlytics api implementations

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-01-30 11:31:46 +01:00
parent b5b9b8d98a
commit 311dda9e18
9 changed files with 227 additions and 2 deletions
@@ -8,6 +8,8 @@
import { AlertApi } from '@backstage/core-plugin-api';
import { alertApiRef } from '@backstage/core-plugin-api';
import { AlertMessage } from '@backstage/core-plugin-api';
import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/core-plugin-api';
import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/core-plugin-api';
import { AnyApiFactory } from '@backstage/core-plugin-api';
import { AnyApiRef } from '@backstage/core-plugin-api';
import { ApiFactory } from '@backstage/core-plugin-api';
@@ -993,6 +995,20 @@ export { identityApiRef };
export { microsoftAuthApiRef };
// @public
export class MultipleAnalyticsApi implements AnalyticsApi_2, AnalyticsApi {
captureEvent(event: AnalyticsEvent_2 | AnalyticsEvent): void;
static fromApis(
actualApis: (AnalyticsApi_2 | AnalyticsApi)[],
): MultipleAnalyticsApi;
}
// @public
export class NoOpAnalyticsApi implements AnalyticsApi_2, AnalyticsApi {
// (undocumented)
captureEvent(_event: AnalyticsEvent_2 | AnalyticsEvent): void;
}
export { OAuthApi };
export { OAuthRequestApi };
@@ -0,0 +1,64 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MultipleAnalyticsApi } from './MultipleAnalyticsApi';
describe('MultipleAnalyticsApi', () => {
const analyticsApiOne = { captureEvent: jest.fn() };
const analyticsApiTwo = { captureEvent: jest.fn() };
const multipleApis = MultipleAnalyticsApi.fromApis([
analyticsApiOne,
analyticsApiTwo,
]);
const event = {
action: 'navivate',
subject: '/path',
context: {
extension: 'App',
pluginId: 'plugin',
routeRef: 'unknown',
},
};
beforeEach(() => {
jest.clearAllMocks();
});
it('forwards events to all apis', () => {
// When an event is captured
multipleApis.captureEvent(event);
// Then both underlying APIs should have received the event
expect(analyticsApiOne.captureEvent).toHaveBeenCalledTimes(1);
expect(analyticsApiOne.captureEvent).toHaveBeenCalledWith(event);
expect(analyticsApiTwo.captureEvent).toHaveBeenCalledTimes(1);
expect(analyticsApiTwo.captureEvent).toHaveBeenCalledWith(event);
});
it('forwards events to all apis even if one throws an error', () => {
// Given one underlying API that throws on capture
analyticsApiOne.captureEvent.mockImplementation(() => {
throw new Error('!!!');
});
// When an event is captured
multipleApis.captureEvent(event);
// Then the other underlying API should have still received the event
expect(analyticsApiTwo.captureEvent).toHaveBeenCalledTimes(1);
expect(analyticsApiTwo.captureEvent).toHaveBeenCalledWith(event);
});
});
@@ -0,0 +1,76 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api';
import {
AnalyticsApi as NewAnalyicsApi,
AnalyticsEvent as NewAnalyicsEvent,
} from '../../definitions';
/**
* An implementation of the AnalyticsApi that can be used to forward analytics
* events to multiple concrete implementations.
*
* @public
*
* @example
*
* ```jsx
* createApiFactory({
* api: analyticsApiRef,
* deps: { configApi: configApiRef, identityApi: identityApiRef, storageApi: storageApiRef },
* factory: ({ configApi, identityApi, storageApi }) =>
* MultipleAnalyticsApi.fromApis([
* VendorAnalyticsApi.fromConfig(configApi, { identityApi }),
* CustomAnalyticsApi.fromConfig(configApi, { identityApi, storageApi }),
* ]),
* });
* ```
*/
export class MultipleAnalyticsApi implements AnalyticsApi, NewAnalyicsApi {
private constructor(
private readonly actualApis: (AnalyticsApi | NewAnalyicsApi)[],
) {}
/**
* Create an AnalyticsApi implementation from an array of concrete
* implementations.
*
* @example
*
* ```jsx
* MultipleAnalyticsApi.fromApis([
* SomeAnalyticsApi.fromConfig(configApi),
* new CustomAnalyticsApi(),
* ]);
* ```
*/
static fromApis(actualApis: (AnalyticsApi | NewAnalyicsApi)[]) {
return new MultipleAnalyticsApi(actualApis);
}
/**
* Forward the event to all configured analytics API implementations.
*/
captureEvent(event: AnalyticsEvent | NewAnalyicsEvent): void {
this.actualApis.forEach(analyticsApi => {
try {
analyticsApi.captureEvent(event as AnalyticsEvent & NewAnalyicsEvent);
} catch {
/* ignored */
}
});
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api';
import {
AnalyticsApi as NewAnalyicsApi,
AnalyticsEvent as NewAnalyicsEvent,
} from '../../definitions';
/**
* Base implementation for the AnalyticsApi that does nothing.
*
* @public
*/
export class NoOpAnalyticsApi implements AnalyticsApi, NewAnalyicsApi {
captureEvent(_event: AnalyticsEvent | NewAnalyicsEvent): void {}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { MultipleAnalyticsApi } from './MultipleAnalyticsApi';
export { NoOpAnalyticsApi } from './NoOpAnalyticsApi';
@@ -0,0 +1,20 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This folder contains implementations for all core APIs.
// Plugins should rely on these APIs for functionality as much as possible.
export * from './AnalyticsApi';
@@ -15,4 +15,5 @@
*/
export * from './definitions';
export * from './implementations';
export * from './system';