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

Release 6.4.5 (#7319)

* White-listing of telemetry params (#7262)

* Drop support for adding telemetry by tagging

This was not used, and would make development of future feature more difficult.

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Introduce white-listing for params of telemetry

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Fix linting

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make misconfigured telemetry for function parameters log the error, and not blow up fatally

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

---------

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Release 6.4.5

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

---------

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
Iku-turso 2023-03-08 16:38:28 +02:00 committed by GitHub
parent 57ce050e11
commit 4df5079cb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 195 additions and 79 deletions

View File

@ -4,7 +4,7 @@
"packages": [ "packages": [
"packages/*" "packages/*"
], ],
"version": "6.4.4", "version": "6.4.5",
"npmClient": "yarn", "npmClient": "yarn",
"npmClientArgs": [ "npmClientArgs": [
"--network-timeout=100000" "--network-timeout=100000"

View File

@ -3,7 +3,7 @@
"productName": "", "productName": "",
"description": "Lens Desktop Core", "description": "Lens Desktop Core",
"homepage": "https://github.com/lensapp/lens", "homepage": "https://github.com/lensapp/lens",
"version": "6.4.4", "version": "6.4.5",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lensapp/lens.git" "url": "git+https://github.com/lensapp/lens.git"

View File

@ -8,6 +8,7 @@ import loggerInjectable from "./logger.injectable";
const logErrorInjectable = getInjectable({ const logErrorInjectable = getInjectable({
id: "log-error", id: "log-error",
instantiate: (di) => di.inject(loggerInjectable).error, instantiate: (di) => di.inject(loggerInjectable).error,
decorable: false,
}); });
export default logErrorInjectable; export default logErrorInjectable;

View File

@ -26,6 +26,8 @@ const loggerInjectable = getInjectable({
silly: (message, ...data) => baseLogger.silly(message, ...data), silly: (message, ...data) => baseLogger.silly(message, ...data),
}; };
}, },
decorable: false,
}); });
export default loggerInjectable; export default loggerInjectable;

View File

@ -8,6 +8,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
import telemetryWhiteListForFunctionsInjectable from "./renderer/telemetry-white-list-for-functions.injectable"; import telemetryWhiteListForFunctionsInjectable from "./renderer/telemetry-white-list-for-functions.injectable";
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
import logErrorInjectable from "../../common/log-error.injectable";
describe("emit-telemetry-from-specific-function-calls", () => { describe("emit-telemetry-from-specific-function-calls", () => {
let di: DiContainer; let di: DiContainer;
@ -22,72 +23,158 @@ describe("emit-telemetry-from-specific-function-calls", () => {
beforeEach(() => { beforeEach(() => {
di.override(telemetryWhiteListForFunctionsInjectable, () => [ di.override(telemetryWhiteListForFunctionsInjectable, () => [
"some-white-listed-function", "some-white-listed-function",
{
id: "some-white-listed-function-with-white-listed-argument",
getParams: (irrelevantArg, arg) => ({ someParam: arg }),
},
{
id: "some-white-listed-function-with-bad-config",
getParams: () => {
throw new Error("some-error-from-bad-configuration");
},
},
]); ]);
emitEventMock = jest.fn(); emitEventMock = jest.fn();
di.override(emitEventInjectable, () => emitEventMock); di.override(emitEventInjectable, () => emitEventMock);
}); });
describe("given instances of white-listed, non-white-listed and tagged functions", () => { describe("given instances of white-listed and non-white-listed functions", () => {
let whiteListedFunctionMock: jest.Mock; let whiteListedFunctionMock: jest.Mock;
let nonWhiteListedFunctionMock: jest.Mock; let nonWhiteListedFunctionMock: jest.Mock;
let taggedFunctionMock: jest.Mock; let whiteListedFunction: jest.Mock;
let injectedWhiteListedFunction: jest.Mock; let whiteListedFunctionWithArgument: jest.Mock;
let injectedNonWhiteListedFunction: jest.Mock; let whiteListedFunctionWithFaultyConfig: jest.Mock;
let injectedTaggedFunction: jest.Mock; let nonWhiteListedFunction: jest.Mock;
let logErrorMock: jest.Mock;
beforeEach(() => { beforeEach(() => {
whiteListedFunctionMock = jest.fn(); whiteListedFunctionMock = jest.fn();
nonWhiteListedFunctionMock = jest.fn(); nonWhiteListedFunctionMock = jest.fn();
taggedFunctionMock = jest.fn(); logErrorMock = jest.fn();
const whiteListedInjectable = getInjectable({ const whiteListedInjectable = getInjectable({
id: "some-white-listed-function", id: "some-white-listed-function",
instantiate: () => whiteListedFunctionMock, instantiate: () => whiteListedFunctionMock,
}); });
const whiteListedInjectableWithArgument = getInjectable({
id: "some-white-listed-function-with-white-listed-argument",
instantiate: () => whiteListedFunctionMock,
});
const whiteListedInjectableWithBadConfig = getInjectable({
id: "some-white-listed-function-with-bad-config",
instantiate: () => whiteListedFunctionMock,
});
const nonWhiteListedInjectable = getInjectable({ const nonWhiteListedInjectable = getInjectable({
id: "some-non-white-listed-function", id: "some-non-white-listed-function",
instantiate: () => nonWhiteListedFunctionMock, instantiate: () => nonWhiteListedFunctionMock,
}); });
const taggedInjectable = getInjectable({
id: "some-tagged-function",
instantiate: () => taggedFunctionMock,
tags: ["emit-telemetry"],
});
runInAction(() => { runInAction(() => {
di.register(whiteListedInjectable); di.register(
di.register(nonWhiteListedInjectable); whiteListedInjectable,
di.register(taggedInjectable); whiteListedInjectableWithArgument,
whiteListedInjectableWithBadConfig,
nonWhiteListedInjectable,
);
}); });
injectedWhiteListedFunction = di.inject(whiteListedInjectable); di.override(logErrorInjectable, () => logErrorMock);
injectedNonWhiteListedFunction = di.inject(nonWhiteListedInjectable);
injectedTaggedFunction = di.inject(taggedInjectable); whiteListedFunction = di.inject(whiteListedInjectable);
whiteListedFunctionWithArgument = di.inject(
whiteListedInjectableWithArgument,
);
whiteListedFunctionWithFaultyConfig = di.inject(
whiteListedInjectableWithBadConfig,
);
nonWhiteListedFunction = di.inject(nonWhiteListedInjectable);
}); });
it("telemetry is not emitted yet", () => { it("telemetry is not emitted yet", () => {
expect(emitEventMock).not.toHaveBeenCalled(); expect(emitEventMock).not.toHaveBeenCalled();
}); });
describe("when the white-listed function is called", () => { it("doesn't log errors, at least yet", () => {
beforeEach(() => { expect(logErrorMock).not.toHaveBeenCalled();
injectedWhiteListedFunction("some-arg", "some-other-arg");
}); });
it("telemetry is emitted in event bus", () => { describe("when a normal white-listed function is called with arguments", () => {
beforeEach(() => {
whiteListedFunction("some-arg", "some-other-arg");
});
it("telemetry is emitted in event bus without the arguments", () => {
expect(emitEventMock).toHaveBeenCalledWith({ expect(emitEventMock).toHaveBeenCalledWith({
destination: "auto-capture", destination: "auto-capture",
action: "telemetry-from-business-action", action: "telemetry-from-business-action",
name: "some-white-listed-function", name: "some-white-listed-function",
params: { args: ["some-arg", "some-other-arg"] },
}); });
}); });
}); });
describe("when the white-listed function is called with MobX reactive content", () => { describe("when a white-listed function with a white-listed argument is called with arguments", () => {
beforeEach(() => {
whiteListedFunctionWithArgument("some-arg", "some-other-arg");
});
it("telemetry is emitted in event bus with the arguments as params", () => {
expect(emitEventMock).toHaveBeenCalledWith({
action: "telemetry-from-business-action",
destination: "auto-capture",
name: "some-white-listed-function-with-white-listed-argument",
params: { someParam: "some-other-arg" },
});
});
});
describe("when a white-listed function with a white-listed argument is called without arguments", () => {
beforeEach(() => {
whiteListedFunctionWithArgument();
});
it("telemetry is emitted in event bus without params", () => {
expect(emitEventMock).toHaveBeenCalledWith({
action: "telemetry-from-business-action",
destination: "auto-capture",
name: "some-white-listed-function-with-white-listed-argument",
params: { someParam: undefined },
});
});
});
describe("given a faulty configuration, when a white-listed function is called", () => {
beforeEach(() => {
whiteListedFunctionWithFaultyConfig();
});
it("telemetry is still emitted in event bus, but with params indicating bad configuration, ", () => {
expect(emitEventMock).toHaveBeenCalledWith({
action: "telemetry-from-business-action",
destination: "auto-capture",
name: "some-white-listed-function-with-bad-config",
params: { error: "Tried to produce params for telemetry, but getParams() threw an error" },
});
});
it("logs error", () => {
expect(logErrorMock).toHaveBeenCalledWith(
'Tried to produce params for telemetry of "some-white-listed-function-with-bad-config", but getParams() threw an error',
expect.objectContaining({ message: "some-error-from-bad-configuration" }),
);
});
});
describe("when a white-listed function with a white-listed argument is called with MobX reactive content", () => {
beforeEach(() => { beforeEach(() => {
const someComputedProperty = computed(() => "some-computed-value"); const someComputedProperty = computed(() => "some-computed-value");
@ -96,22 +183,23 @@ describe("emit-telemetry-from-specific-function-calls", () => {
someComputedProperty, someComputedProperty,
}; };
injectedWhiteListedFunction(someObservable); whiteListedFunctionWithArgument(
"irrelevant-argument",
someObservable,
);
}); });
it("telemetry is emitted in event bus without MobX internals or computeds", () => { it("telemetry is emitted in event bus without MobX internals or computeds", () => {
expect(emitEventMock).toHaveBeenCalledWith({ expect(emitEventMock).toHaveBeenCalledWith({
destination: "auto-capture", destination: "auto-capture",
action: "telemetry-from-business-action", action: "telemetry-from-business-action",
name: "some-white-listed-function", name: "some-white-listed-function-with-white-listed-argument",
params: { params: {
args: [ someParam: {
{
someStaticProperty: "some-static-value", someStaticProperty: "some-static-value",
someComputedProperty: "some-computed-value", someComputedProperty: "some-computed-value",
}, },
],
}, },
}); });
}); });
@ -119,28 +207,13 @@ describe("emit-telemetry-from-specific-function-calls", () => {
describe("when the non-white-listed function is called", () => { describe("when the non-white-listed function is called", () => {
beforeEach(() => { beforeEach(() => {
injectedNonWhiteListedFunction(); nonWhiteListedFunction();
}); });
it("telemetry is not emitted", () => { it("telemetry is not emitted", () => {
expect(emitEventMock).not.toHaveBeenCalled(); 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

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import emitEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import emitEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
import { toJS, observable } from "mobx"; import { observable, toJS } from "mobx";
const emitTelemetryInjectable = getInjectable({ const emitTelemetryInjectable = getInjectable({
id: "emit-telemetry", id: "emit-telemetry",
@ -12,12 +12,12 @@ const emitTelemetryInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const emitEvent = di.inject(emitEventInjectable); const emitEvent = di.inject(emitEventInjectable);
return ({ action, args }: { action: string; args: any[] }) => { return ({ action, params }: { action: string; params?: object }) => {
emitEvent({ emitEvent({
destination: "auto-capture", destination: "auto-capture",
action: "telemetry-from-business-action", action: "telemetry-from-business-action",
name: action, name: action,
params: { args: toJS(observable(args)) }, ...(params ? { params: toJS(observable(params)) } : {}),
}); });
}; };
}, },

View File

@ -2,33 +2,34 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { import type { DiContainerForInjection } from "@ogre-tools/injectable";
DiContainerForInjection,
Injectable,
} from "@ogre-tools/injectable";
import { import {
lifecycleEnum,
getInjectable, getInjectable,
instantiationDecoratorToken, instantiationDecoratorToken,
lifecycleEnum,
} from "@ogre-tools/injectable"; } from "@ogre-tools/injectable";
import assert from "assert";
import assert from "assert";
import { isFunction } from "lodash/fp"; import { isFunction } from "lodash/fp";
import emitTelemetryInjectable from "./emit-telemetry.injectable"; import emitTelemetryInjectable from "./emit-telemetry.injectable";
import type { WhiteListItem } from "./telemetry-white-list-for-functions.injectable";
import telemetryWhiteListForFunctionsInjectable from "./telemetry-white-list-for-functions.injectable"; import telemetryWhiteListForFunctionsInjectable from "./telemetry-white-list-for-functions.injectable";
import logErrorInjectable from "../../../common/log-error.injectable";
const telemetryDecoratorInjectable = getInjectable({ const telemetryDecoratorInjectable = getInjectable({
id: "telemetry-decorator", id: "telemetry-decorator",
instantiate: (diForDecorator) => { instantiate: (diForDecorator) => {
const emitTelemetry = diForDecorator.inject(emitTelemetryInjectable); const emitTelemetry = diForDecorator.inject(emitTelemetryInjectable);
const logError = diForDecorator.inject(logErrorInjectable);
const whiteList = diForDecorator.inject( const whiteList = diForDecorator.inject(
telemetryWhiteListForFunctionsInjectable, telemetryWhiteListForFunctionsInjectable,
); );
const shouldEmitTelemetry = shouldEmitTelemetryFor(whiteList); const whiteListMap = getWhiteListMap(whiteList);
return { return {
decorate: decorate:
@ -42,8 +43,31 @@ const telemetryDecoratorInjectable = getInjectable({
assert(currentContext); assert(currentContext);
if (shouldEmitTelemetry(currentContext.injectable)) { const whiteListed = whiteListMap.get(
emitTelemetry({ action: currentContext.injectable.id, args }); currentContext.injectable.id,
);
if (whiteListed) {
let params;
try {
params = whiteListed.getParams(...args);
} catch (e) {
params = {
error:
"Tried to produce params for telemetry, but getParams() threw an error",
};
logError(
`Tried to produce params for telemetry of "${currentContext.injectable.id}", but getParams() threw an error`,
e,
);
}
emitTelemetry({
action: currentContext.injectable.id,
params,
});
} }
return instance(...args); return instance(...args);
@ -61,9 +85,23 @@ const telemetryDecoratorInjectable = getInjectable({
injectionToken: instantiationDecoratorToken, injectionToken: instantiationDecoratorToken,
}); });
const shouldEmitTelemetryFor = const getWhiteListMap = (whiteList: WhiteListItem[]) =>
(whiteList: string[]) => (injectable: Injectable<any, any, any>) => new Map(
injectable.tags?.includes("emit-telemetry") || whiteList.map((item) =>
whiteList.includes(injectable.id); typeof item === "string"
? [
item,
{
getParams: () => undefined,
},
]
: [
item.id,
{
getParams: item.getParams,
},
],
),
);
export default telemetryDecoratorInjectable; export default telemetryDecoratorInjectable;

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { AppEvent } from "../../../common/app-event-bus/event-bus";
const navigateTo = [ const navigateTo = [
"navigate-to-preference-tab-id", "navigate-to-preference-tab-id",
@ -88,21 +89,19 @@ const extensions = [
"uninstall-extension", "uninstall-extension",
]; ];
const externalActions = [ const externalActions = ["open-link-in-browser"];
"open-link-in-browser",
];
const uiInteraction = [ const uiInteraction = ["show-details"];
"show-details",
];
const terminal = [ const terminal = ["create-terminal-tab"];
"create-terminal-tab",
]; export type WhiteListItem =
| string
| { id: string; getParams: (...args: unknown[]) => AppEvent["params"] };
const telemetryWhiteListForFunctionsInjectable = getInjectable({ const telemetryWhiteListForFunctionsInjectable = getInjectable({
id: "telemetry-white-list-for-functions", id: "telemetry-white-list-for-functions",
instantiate: () => [ instantiate: (): WhiteListItem[] => [
...navigateTo, ...navigateTo,
...helmInjectableIds, ...helmInjectableIds,
...kubeConfigActions, ...kubeConfigActions,

View File

@ -30,6 +30,7 @@ const consoleLoggerTransportInjectable = getInjectable({
), ),
}), }),
injectionToken: loggerTransportInjectionToken, injectionToken: loggerTransportInjectionToken,
decorable: false,
}); });
export default consoleLoggerTransportInjectable; export default consoleLoggerTransportInjectable;

View File

@ -23,6 +23,7 @@ const fileLoggerTranportInjectable = getInjectable({
tailable: true, tailable: true,
}), }),
injectionToken: loggerTransportInjectionToken, injectionToken: loggerTransportInjectionToken,
decorable: false,
}); });
export default fileLoggerTranportInjectable; export default fileLoggerTranportInjectable;

View File

@ -10,6 +10,7 @@ const browserLoggerTransportInjectable = getInjectable({
id: "browser-logger-transport", id: "browser-logger-transport",
instantiate: () => new BrowserConsole(), instantiate: () => new BrowserConsole(),
injectionToken: loggerTransportInjectionToken, injectionToken: loggerTransportInjectionToken,
decorable: false,
}); });
export default browserLoggerTransportInjectable; export default browserLoggerTransportInjectable;

View File

@ -2,7 +2,7 @@
"name": "@k8slens/extensions", "name": "@k8slens/extensions",
"productName": "OpenLens extensions", "productName": "OpenLens extensions",
"description": "OpenLens - Open Source Kubernetes IDE: extensions", "description": "OpenLens - Open Source Kubernetes IDE: extensions",
"version": "6.4.4", "version": "6.4.5",
"copyright": "© 2022 OpenLens Authors", "copyright": "© 2022 OpenLens Authors",
"license": "MIT", "license": "MIT",
"main": "dist/extension-api.js", "main": "dist/extension-api.js",
@ -26,7 +26,7 @@
"prepare:dev": "yarn run build" "prepare:dev": "yarn run build"
}, },
"dependencies": { "dependencies": {
"@k8slens/core": "^6.4.4" "@k8slens/core": "^6.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.6", "@types/node": "^16.18.6",

View File

@ -4,7 +4,7 @@
"productName": "OpenLens", "productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes", "description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens", "homepage": "https://github.com/lensapp/lens",
"version": "6.4.4", "version": "6.4.5",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lensapp/lens.git" "url": "git+https://github.com/lensapp/lens.git"
@ -192,7 +192,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@k8slens/core": "^6.4.4", "@k8slens/core": "^6.4.5",
"@k8slens/ensure-binaries": "^6.4.0-beta.16", "@k8slens/ensure-binaries": "^6.4.0-beta.16",
"@k8slens/generate-tray-icons": "^6.4.0-beta.16", "@k8slens/generate-tray-icons": "^6.4.0-beta.16",
"@ogre-tools/fp": "^12.0.1", "@ogre-tools/fp": "^12.0.1",