1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Implement a decorator to emit telemetry for any specified function-injectable

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
Iku-turso 2022-08-31 14:43:12 +03:00
parent e5df27389e
commit 7e9e48e631
5 changed files with 222 additions and 0 deletions

View File

@ -8,6 +8,7 @@ import appEventBusInjectable from "./app-event-bus.injectable";
const emitEventInjectable = getInjectable({ const emitEventInjectable = getInjectable({
id: "emit-event", id: "emit-event",
instantiate: (di) => di.inject(appEventBusInjectable).emit, instantiate: (di) => di.inject(appEventBusInjectable).emit,
decorable: false,
}); });
export default emitEventInjectable; export default emitEventInjectable;

View File

@ -0,0 +1,116 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
import telemetryWhiteListForFunctionsInjectable from "./renderer/telemetry-white-list-for-functions.injectable";
import { runInAction } from "mobx";
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
describe("sending-telemetry-from-white-listed-function-calls", () => {
let di: DiContainer;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
});
describe("given a telemetry white-list for injectables which instantiate a function", () => {
let emitEventMock: jest.Mock;
beforeEach(() => {
di.override(telemetryWhiteListForFunctionsInjectable, () => [
"some-white-listed-function",
]);
emitEventMock = jest.fn();
di.override(emitEventInjectable, () => emitEventMock);
});
describe("given instances of white-listed, non-white-listed and tagged functions", () => {
let whiteListedFunctionMock: jest.Mock;
let nonWhiteListedFunctionMock: jest.Mock;
let taggedFunctionMock: jest.Mock;
let injectedWhiteListedFunction: jest.Mock;
let injectedNonWhiteListedFunction: jest.Mock;
let injectedTaggedFunction: jest.Mock;
beforeEach(() => {
whiteListedFunctionMock = jest.fn();
nonWhiteListedFunctionMock = jest.fn();
taggedFunctionMock = jest.fn();
const whiteListedInjectable = getInjectable({
id: "some-white-listed-function",
instantiate: () => whiteListedFunctionMock,
});
const nonWhiteListedInjectable = getInjectable({
id: "some-non-white-listed-function",
instantiate: () => nonWhiteListedFunctionMock,
});
const taggedInjectable = getInjectable({
id: "some-tagged-function",
instantiate: () => taggedFunctionMock,
tags: ["emit-telemetry"],
});
runInAction(() => {
di.register(whiteListedInjectable);
di.register(nonWhiteListedInjectable);
di.register(taggedInjectable);
});
injectedWhiteListedFunction = di.inject(whiteListedInjectable);
injectedNonWhiteListedFunction = di.inject(nonWhiteListedInjectable);
injectedTaggedFunction = di.inject(taggedInjectable);
});
it("telemetry is not emitted yet", () => {
expect(emitEventMock).not.toHaveBeenCalled();
});
describe("when the white-listed function is called", () => {
beforeEach(() => {
injectedWhiteListedFunction("some-arg", "some-other-arg");
});
it("telemetry is emitted in event bus", () => {
expect(emitEventMock).toHaveBeenCalledWith({
destination: "auto-capture",
action: "telemetry-from-business-action",
name: "some-white-listed-function",
params: { args: ["some-arg", "some-other-arg"] },
});
});
});
describe("when the non-white-listed function is called", () => {
beforeEach(() => {
injectedNonWhiteListedFunction();
});
it("telemetry is not emitted", () => {
expect(emitEventMock).not.toHaveBeenCalled();
});
});
describe("when the tagged, but not white-listed function is called", () => {
beforeEach(() => {
injectedTaggedFunction("some-arg", "some-other-arg");
});
it("telemetry is emitted in event bus", () => {
expect(emitEventMock).toHaveBeenCalledWith({
destination: "auto-capture",
action: "telemetry-from-business-action",
name: "some-tagged-function",
params: { args: ["some-arg", "some-other-arg"] },
});
});
});
});
});
});

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import emitEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
const emitTelemetryInjectable = getInjectable({
id: "emit-telemetry",
instantiate: (di) => {
const emitEvent = di.inject(emitEventInjectable);
return ({ action, args }: { action: string; args: any[] }) => {
emitEvent({
destination: "auto-capture",
action: "telemetry-from-business-action",
name: action,
params: { args },
});
};
},
decorable: false,
});
export default emitTelemetryInjectable;

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainerForInjection } from "@ogre-tools/injectable";
import {
lifecycleEnum,
getInjectable,
instantiationDecoratorToken,
} from "@ogre-tools/injectable";
import assert from "assert";
import { isFunction } from "lodash/fp";
import emitTelemetryInjectable from "./emit-telemetry.injectable";
import telemetryWhiteListForFunctionsInjectable from "./telemetry-white-list-for-functions.injectable";
const telemetryDecoratorInjectable = getInjectable({
id: "telemetry-decorator",
instantiate: (diForDecorator) => {
const emitTelemetry = diForDecorator.inject(emitTelemetryInjectable);
const whiteList = diForDecorator.inject(
telemetryWhiteListForFunctionsInjectable,
);
return {
decorate:
(instantiateToBeDecorated: any) =>
(di: DiContainerForInjection, instantiationParameter: any) => {
const instance = instantiateToBeDecorated(di, instantiationParameter);
if (isFunction(instance)) {
return (...args: any[]) => {
const currentContext = di.context.at(-1);
assert(currentContext);
if (shouldEmitTelemetry(currentContext, whiteList)) {
emitTelemetry({ action: currentContext.injectable.id, args });
}
return instance(...args);
};
}
return instance;
},
};
},
decorable: false,
// Todo: this is required because of imperfect typing in injectable.
lifecycle: lifecycleEnum.singleton,
injectionToken: instantiationDecoratorToken,
});
const shouldEmitTelemetry = (currentContext: any, whiteList: any) => (
currentContext.injectable.tags?.includes("emit-telemetry") ||
whiteList.includes(currentContext.injectable.id)
);
export default telemetryDecoratorInjectable;

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
const telemetryWhiteListForFunctionsInjectable = getInjectable({
id: "telemetry-white-list-for-functions",
instantiate: () => ["some-placeholder-injectable-id"],
decorable: false,
});
export default telemetryWhiteListForFunctionsInjectable;