mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Persist apiVersion when editing resources in monaco (#4406)
* Persist apiVersion when editing resources in monaco - Use a new custom k8slens prefixed label - Means that users aren't surprised when they use lens to update a resource to a new apiVersionWithGroup - Doesn't touch the versions in the stores Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix: Fix lint issues Signed-off-by: Sebastian Malton <sebastian@malton.name> * chore: make lint not bail on failure Signed-off-by: Sebastian Malton <sebastian@malton.name> * chore: Run lint:fix on all files Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
058494bc73
commit
807f98ed1b
@ -21,7 +21,7 @@
|
|||||||
"postdev": "lerna watch -- lerna run build --stream --scope \\$LERNA_PACKAGE_NAME",
|
"postdev": "lerna watch -- lerna run build --stream --scope \\$LERNA_PACKAGE_NAME",
|
||||||
"prestart-dev": "cd packages/open-lens && rimraf static/build/ && npm run build:tray-icons && npm run download:binaries",
|
"prestart-dev": "cd packages/open-lens && rimraf static/build/ && npm run build:tray-icons && npm run download:binaries",
|
||||||
"start-dev": "lerna run start",
|
"start-dev": "lerna run start",
|
||||||
"lint": "lerna run lint --stream",
|
"lint": "lerna run lint --stream --no-bail",
|
||||||
"lint:fix": "lerna run lint:fix --stream",
|
"lint:fix": "lerna run lint:fix --stream",
|
||||||
"mkdocs:serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
"mkdocs:serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
||||||
"mkdocs:verify": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",
|
"mkdocs:verify": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import { filter, isString } from "lodash/fp";
|
import { filter, isString } from "lodash/fp";
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import {
|
import { Binding, KeyboardShortcut, keyboardShortcutInjectionToken } from "./keyboard-shortcut-injection-token";
|
||||||
Binding,
|
|
||||||
KeyboardShortcut,
|
|
||||||
keyboardShortcutInjectionToken,
|
|
||||||
} from "./keyboard-shortcut-injection-token";
|
|
||||||
import platformInjectable from "./platform.injectable";
|
import platformInjectable from "./platform.injectable";
|
||||||
|
|
||||||
export type InvokeShortcut = (event: KeyboardEvent) => void;
|
export type InvokeShortcut = (event: KeyboardEvent) => void;
|
||||||
@ -46,8 +42,7 @@ const toBindingWithDefaults = (binding: Binding) =>
|
|||||||
...binding,
|
...binding,
|
||||||
};
|
};
|
||||||
|
|
||||||
const toShortcutsWithMatchingBinding =
|
const toShortcutsWithMatchingBinding = (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => {
|
||||||
(event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => {
|
|
||||||
const binding = toBindingWithDefaults(shortcut.binding);
|
const binding = toBindingWithDefaults(shortcut.binding);
|
||||||
|
|
||||||
const shiftModifierMatches = binding.shift === event.shiftKey;
|
const shiftModifierMatches = binding.shift === event.shiftKey;
|
||||||
@ -55,11 +50,9 @@ const toShortcutsWithMatchingBinding =
|
|||||||
|
|
||||||
const isMac = platform === "darwin";
|
const isMac = platform === "darwin";
|
||||||
|
|
||||||
const ctrlModifierMatches =
|
const ctrlModifierMatches = binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey);
|
||||||
binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey);
|
|
||||||
|
|
||||||
const metaModifierMatches =
|
const metaModifierMatches = binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey);
|
||||||
binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
event.code === binding.code &&
|
event.code === binding.code &&
|
||||||
@ -68,7 +61,7 @@ const toShortcutsWithMatchingBinding =
|
|||||||
altModifierMatches &&
|
altModifierMatches &&
|
||||||
metaModifierMatches
|
metaModifierMatches
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const invokeShortcutInjectable = getInjectable({
|
const invokeShortcutInjectable = getInjectable({
|
||||||
id: "invoke-shortcut",
|
id: "invoke-shortcut",
|
||||||
|
|||||||
@ -26,10 +26,7 @@ const NonInjectedKeyboardShortcutListener = ({
|
|||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KeyboardShortcutListener = withInjectables<
|
export const KeyboardShortcutListener = withInjectables<Dependencies, KeyboardShortcutListenerProps>(
|
||||||
Dependencies,
|
|
||||||
KeyboardShortcutListenerProps
|
|
||||||
>(
|
|
||||||
NonInjectedKeyboardShortcutListener,
|
NonInjectedKeyboardShortcutListener,
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -175,8 +175,7 @@ describe("keyboard-shortcuts", () => {
|
|||||||
shouldCallCallback: true,
|
shouldCallCallback: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario:
|
scenario: "given shortcut with shift modifier, when shortcut is pressed, calls the callback",
|
||||||
"given shortcut with shift modifier, when shortcut is pressed, calls the callback",
|
|
||||||
|
|
||||||
binding: { shift: true, code: "F1" },
|
binding: { shift: true, code: "F1" },
|
||||||
keyboard: "{Shift>}[F1]",
|
keyboard: "{Shift>}[F1]",
|
||||||
|
|||||||
@ -109,7 +109,25 @@ export function parseKubeApi(path: string): IKubeApiParsed {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createKubeApiURL({ apiPrefix = "/apis", resource, apiVersion, name, namespace }: IKubeApiLinkRef): string {
|
function isIKubeApiParsed(refOrParsed: IKubeApiLinkRef | IKubeApiParsed): refOrParsed is IKubeApiParsed {
|
||||||
|
return "apiGroup" in refOrParsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createKubeApiURL(linkRef: IKubeApiLinkRef): string;
|
||||||
|
export function createKubeApiURL(linkParsed: IKubeApiParsed): string;
|
||||||
|
|
||||||
|
export function createKubeApiURL(ref: IKubeApiLinkRef | IKubeApiParsed): string {
|
||||||
|
if (isIKubeApiParsed(ref)) {
|
||||||
|
return createKubeApiURL({
|
||||||
|
apiPrefix: ref.apiPrefix,
|
||||||
|
resource: ref.resource,
|
||||||
|
name: ref.name,
|
||||||
|
namespace: ref.namespace,
|
||||||
|
apiVersion: `${ref.apiGroup}/${ref.apiVersion}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { apiPrefix = "/apis", resource, apiVersion, name, namespace } = ref;
|
||||||
const parts = [apiPrefix, apiVersion];
|
const parts = [apiPrefix, apiVersion];
|
||||||
|
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
|
|||||||
@ -193,7 +193,7 @@ export interface KubeApiWatchOptions<Object extends KubeObject, Data extends Kub
|
|||||||
|
|
||||||
export type KubeApiPatchType = "merge" | "json" | "strategic";
|
export type KubeApiPatchType = "merge" | "json" | "strategic";
|
||||||
|
|
||||||
const patchTypeHeaders: Record<KubeApiPatchType, string> = {
|
export const patchTypeHeaders: Record<KubeApiPatchType, string> = {
|
||||||
"merge": "application/merge-patch+json",
|
"merge": "application/merge-patch+json",
|
||||||
"json": "application/json-patch+json",
|
"json": "application/json-patch+json",
|
||||||
"strategic": "application/strategic-merge-patch+json",
|
"strategic": "application/strategic-merge-patch+json",
|
||||||
|
|||||||
@ -437,6 +437,19 @@ const resourceApplierAnnotationsForFiltering = [
|
|||||||
|
|
||||||
const filterOutResourceApplierAnnotations = (label: string) => !resourceApplierAnnotationsForFiltering.some(key => label.startsWith(key));
|
const filterOutResourceApplierAnnotations = (label: string) => !resourceApplierAnnotationsForFiltering.some(key => label.startsWith(key));
|
||||||
|
|
||||||
|
export interface RawKubeObject<
|
||||||
|
Metadata extends KubeObjectMetadata = KubeObjectMetadata,
|
||||||
|
Status = Record<string, unknown>,
|
||||||
|
Spec = Record<string, unknown>,
|
||||||
|
> {
|
||||||
|
apiVersion: string;
|
||||||
|
kind: string;
|
||||||
|
metadata: Metadata;
|
||||||
|
status?: Status;
|
||||||
|
spec?: Spec;
|
||||||
|
managedFields?: any;
|
||||||
|
}
|
||||||
|
|
||||||
export class KubeObject<
|
export class KubeObject<
|
||||||
Metadata extends KubeObjectMetadata<KubeObjectScope> = KubeObjectMetadata<KubeObjectScope>,
|
Metadata extends KubeObjectMetadata<KubeObjectScope> = KubeObjectMetadata<KubeObjectScope>,
|
||||||
Status = unknown,
|
Status = unknown,
|
||||||
@ -538,23 +551,6 @@ export class KubeObject<
|
|||||||
return Object.entries(labels).map(([name, value]) => `${name}=${value}`);
|
return Object.entries(labels).map(([name, value]) => `${name}=${value}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* These must be RFC6902 compliant paths
|
|
||||||
*/
|
|
||||||
private static readonly nonEditablePathPrefixes = [
|
|
||||||
"/metadata/managedFields",
|
|
||||||
"/status",
|
|
||||||
];
|
|
||||||
private static readonly nonEditablePaths = new Set([
|
|
||||||
"/apiVersion",
|
|
||||||
"/kind",
|
|
||||||
"/metadata/name",
|
|
||||||
"/metadata/selfLink",
|
|
||||||
"/metadata/resourceVersion",
|
|
||||||
"/metadata/uid",
|
|
||||||
...KubeObject.nonEditablePathPrefixes,
|
|
||||||
]);
|
|
||||||
|
|
||||||
constructor(data: KubeJsonApiData<Metadata, Status, Spec>) {
|
constructor(data: KubeJsonApiData<Metadata, Status, Spec>) {
|
||||||
if (typeof data !== "object") {
|
if (typeof data !== "object") {
|
||||||
throw new TypeError(`Cannot create a KubeObject from ${typeof data}`);
|
throw new TypeError(`Cannot create a KubeObject from ${typeof data}`);
|
||||||
@ -684,18 +680,6 @@ export class KubeObject<
|
|||||||
* @deprecated use KubeApi.patch instead
|
* @deprecated use KubeApi.patch instead
|
||||||
*/
|
*/
|
||||||
async patch(patch: Patch): Promise<KubeJsonApiData | null> {
|
async patch(patch: Patch): Promise<KubeJsonApiData | null> {
|
||||||
for (const op of patch) {
|
|
||||||
if (KubeObject.nonEditablePaths.has(op.path)) {
|
|
||||||
throw new Error(`Failed to update ${this.kind}: JSON pointer ${op.path} has been modified`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const pathPrefix of KubeObject.nonEditablePathPrefixes) {
|
|
||||||
if (op.path.startsWith(`${pathPrefix}/`)) {
|
|
||||||
throw new Error(`Failed to update ${this.kind}: Child JSON pointer of ${op.path} has been modified`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const di = getLegacyGlobalDiForExtensionApi();
|
const di = getLegacyGlobalDiForExtensionApi();
|
||||||
const requestKubeObjectPatch = di.inject(requestKubeObjectPatchInjectable);
|
const requestKubeObjectPatch = di.inject(requestKubeObjectPatchInjectable);
|
||||||
const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
|
const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
|
||||||
|
|||||||
@ -7469,6 +7469,8 @@ metadata:
|
|||||||
selfLink: /apis/some-api-version/namespaces/some-uid
|
selfLink: /apis/some-api-version/namespaces/some-uid
|
||||||
somePropertyToBeRemoved: some-value
|
somePropertyToBeRemoved: some-value
|
||||||
somePropertyToBeChanged: some-old-value
|
somePropertyToBeChanged: some-old-value
|
||||||
|
labels:
|
||||||
|
k8slens-edit-resource-version: some-api-version
|
||||||
|
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,8 +12,6 @@ import createEditResourceTabInjectable from "../../../renderer/components/dock/e
|
|||||||
import getRandomIdForEditResourceTabInjectable from "../../../renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable";
|
import getRandomIdForEditResourceTabInjectable from "../../../renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable";
|
||||||
import type { AsyncFnMock } from "@async-fn/jest";
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
import asyncFn from "@async-fn/jest";
|
import asyncFn from "@async-fn/jest";
|
||||||
import type { CallForResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable";
|
|
||||||
import callForResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable";
|
|
||||||
import type { CallForPatchResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable";
|
import type { CallForPatchResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable";
|
||||||
import callForPatchResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable";
|
import callForPatchResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable";
|
||||||
import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable";
|
import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable";
|
||||||
@ -23,6 +21,8 @@ import showErrorNotificationInjectable from "../../../renderer/components/notifi
|
|||||||
import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable";
|
import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable";
|
||||||
import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
|
import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
|
||||||
import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable";
|
import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable";
|
||||||
|
import type { CallForResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable";
|
||||||
|
import callForResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable";
|
||||||
|
|
||||||
describe("cluster/namespaces - edit namespace from new tab", () => {
|
describe("cluster/namespaces - edit namespace from new tab", () => {
|
||||||
let builder: ApplicationBuilder;
|
let builder: ApplicationBuilder;
|
||||||
@ -225,10 +225,16 @@ metadata:
|
|||||||
expect(rendered.baseElement).toMatchSnapshot();
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls for save with empty values", () => {
|
it("calls for save with just the adding version label", () => {
|
||||||
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
||||||
someNamespace,
|
someNamespace,
|
||||||
[],
|
[{
|
||||||
|
op: "add",
|
||||||
|
path: "/metadata/labels",
|
||||||
|
value: {
|
||||||
|
"k8slens-edit-resource-version": "some-api-version",
|
||||||
|
},
|
||||||
|
}],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -532,6 +538,13 @@ metadata:
|
|||||||
path: "/metadata/someAddedProperty",
|
path: "/metadata/someAddedProperty",
|
||||||
value: "some-new-value",
|
value: "some-new-value",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
op: "add",
|
||||||
|
path: "/metadata/labels",
|
||||||
|
value: {
|
||||||
|
"k8slens-edit-resource-version": "some-api-version",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
op: "replace",
|
op: "replace",
|
||||||
path: "/metadata/somePropertyToBeChanged",
|
path: "/metadata/somePropertyToBeChanged",
|
||||||
@ -759,7 +772,7 @@ metadata:
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when selecting to save, calls for save of second namespace", () => {
|
it("when selecting to save, calls for save of second namespace with just the add edit version label", () => {
|
||||||
callForPatchResourceMock.mockClear();
|
callForPatchResourceMock.mockClear();
|
||||||
|
|
||||||
const saveButton = rendered.getByTestId(
|
const saveButton = rendered.getByTestId(
|
||||||
@ -770,7 +783,13 @@ metadata:
|
|||||||
|
|
||||||
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
||||||
someOtherNamespace,
|
someOtherNamespace,
|
||||||
[],
|
[{
|
||||||
|
op: "add",
|
||||||
|
path: "/metadata/labels",
|
||||||
|
value: {
|
||||||
|
"k8slens-edit-resource-version": "some-api-version",
|
||||||
|
},
|
||||||
|
}],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -826,7 +845,7 @@ metadata:
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when selecting to save, calls for save of first namespace", () => {
|
it("when selecting to save, calls for save of first namespace with just the new edit version label", () => {
|
||||||
callForPatchResourceMock.mockClear();
|
callForPatchResourceMock.mockClear();
|
||||||
|
|
||||||
const saveButton = rendered.getByTestId(
|
const saveButton = rendered.getByTestId(
|
||||||
@ -837,7 +856,13 @@ metadata:
|
|||||||
|
|
||||||
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
expect(callForPatchResourceMock).toHaveBeenCalledWith(
|
||||||
someNamespace,
|
someNamespace,
|
||||||
[],
|
[ {
|
||||||
|
op: "add",
|
||||||
|
path: "/metadata/labels",
|
||||||
|
value: {
|
||||||
|
"k8slens-edit-resource-version": "some-api-version",
|
||||||
|
},
|
||||||
|
}],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
.TableCell {
|
.TableCell {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: $margin * 2;
|
margin-left: $margin * 2;
|
||||||
}
|
}
|
||||||
@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
|||||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||||
import type { DiRender } from "../test-utils/renderFor";
|
import type { DiRender } from "../test-utils/renderFor";
|
||||||
import { renderFor } from "../test-utils/renderFor";
|
import { renderFor } from "../test-utils/renderFor";
|
||||||
import { HpaDetails } from "./hpa-details";
|
import { HorizontalPodAutoscalerDetails } from "./details";
|
||||||
|
|
||||||
jest.mock("react-router-dom", () => ({
|
jest.mock("react-router-dom", () => ({
|
||||||
Link: ({ children }: { children: React.ReactNode }) => children,
|
Link: ({ children }: { children: React.ReactNode }) => children,
|
||||||
@ -62,7 +62,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
const hpa = new HorizontalPodAutoscaler(hpaV2);
|
const hpa = new HorizontalPodAutoscaler(hpaV2);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.baseElement).toMatchSnapshot();
|
expect(result.baseElement).toMatchSnapshot();
|
||||||
@ -72,7 +72,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
const hpa = new HorizontalPodAutoscaler(hpaV2);
|
const hpa = new HorizontalPodAutoscaler(hpaV2);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.queryByTestId("hpa-metrics")).toBeNull();
|
expect(result.queryByTestId("hpa-metrics")).toBeNull();
|
||||||
@ -101,7 +101,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("CPU Utilization percentage")).toBeInTheDocument();
|
expect(result.getByText("CPU Utilization percentage")).toBeInTheDocument();
|
||||||
@ -131,7 +131,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument();
|
expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument();
|
||||||
@ -160,7 +160,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument();
|
expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument();
|
||||||
@ -191,7 +191,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument();
|
expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument();
|
||||||
@ -216,7 +216,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument();
|
expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument();
|
||||||
@ -252,7 +252,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText(/requests-per-second/)).toHaveTextContent("requests-per-second onService/nginx");
|
expect(result.getByText(/requests-per-second/)).toHaveTextContent("requests-per-second onService/nginx");
|
||||||
@ -277,7 +277,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("requests-per-second")).toBeInTheDocument();
|
expect(result.getByText("requests-per-second")).toBeInTheDocument();
|
||||||
@ -311,7 +311,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument();
|
expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument();
|
||||||
@ -339,7 +339,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument();
|
expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument();
|
||||||
@ -368,7 +368,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.baseElement).toMatchSnapshot();
|
expect(result.baseElement).toMatchSnapshot();
|
||||||
@ -398,7 +398,7 @@ describe("<HpaDetails/>", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
result = render(
|
result = render(
|
||||||
<HpaDetails object={hpa} />,
|
<HorizontalPodAutoscalerDetails object={hpa} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.baseElement).toMatchSnapshot();
|
expect(result.baseElement).toMatchSnapshot();
|
||||||
@ -3,7 +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 "./hpa-details.scss";
|
import "./details.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
@ -22,8 +22,8 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
|||||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
|
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
|
||||||
import loggerInjectable from "../../../common/logger.injectable";
|
import loggerInjectable from "../../../common/logger.injectable";
|
||||||
import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable";
|
import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable";
|
||||||
import { getMetricName } from "./get-hpa-metric-name";
|
import { getMetricName } from "./get-metric-name";
|
||||||
|
|
||||||
export interface HpaDetailsProps extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
|
export interface HpaDetailsProps extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ interface Dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependencies> {
|
class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<HpaDetailsProps & Dependencies> {
|
||||||
private renderTargetLink(target: HorizontalPodAutoscalerMetricTarget | undefined) {
|
private renderTargetLink(target: HorizontalPodAutoscalerMetricTarget | undefined) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return null;
|
return null;
|
||||||
@ -177,7 +177,7 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HpaDetails = withInjectables<Dependencies, HpaDetailsProps>(NonInjectedHpaDetails, {
|
export const HorizontalPodAutoscalerDetails = withInjectables<Dependencies, HpaDetailsProps>(NonInjectedHorizontalPodAutoscalerDetails, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
apiManager: di.inject(apiManagerInjectable),
|
apiManager: di.inject(apiManagerInjectable),
|
||||||
@ -5,9 +5,9 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { HorizontalPodAutoscaler, HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricStatus } from "../../../common/k8s-api/endpoints";
|
import type { HorizontalPodAutoscaler, HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricStatus } from "../../../common/k8s-api/endpoints";
|
||||||
import { HpaMetricType } from "../../../common/k8s-api/endpoints";
|
import { HpaMetricType } from "../../../common/k8s-api/endpoints";
|
||||||
import { getMetricName } from "./get-hpa-metric-name";
|
import { getMetricName } from "./get-metric-name";
|
||||||
import { HorizontalPodAutoscalerV1MetricParser } from "./hpa-v1-metric-parser";
|
import { HorizontalPodAutoscalerV1MetricParser } from "./metric-parser-v1";
|
||||||
import { HorizontalPodAutoscalerV2MetricParser } from "./hpa-v2-metric-parser";
|
import { HorizontalPodAutoscalerV2MetricParser } from "./metric-parser-v2";
|
||||||
|
|
||||||
type Parser = HorizontalPodAutoscalerV1MetricParser | HorizontalPodAutoscalerV2MetricParser;
|
type Parser = HorizontalPodAutoscalerV1MetricParser | HorizontalPodAutoscalerV2MetricParser;
|
||||||
|
|
||||||
@ -3,5 +3,5 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./hpa";
|
export * from "./list-view";
|
||||||
export * from "./hpa-details";
|
export * from "./details";
|
||||||
|
|||||||
@ -3,7 +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 "./hpa.scss";
|
import "./list-view.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
@ -17,7 +17,7 @@ import { KubeObjectAge } from "../kube-object/age";
|
|||||||
import type { HorizontalPodAutoscalerStore } from "./store";
|
import type { HorizontalPodAutoscalerStore } from "./store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import horizontalPodAutoscalerStoreInjectable from "./store.injectable";
|
import horizontalPodAutoscalerStoreInjectable from "./store.injectable";
|
||||||
import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable";
|
import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable";
|
||||||
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
|
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
|
||||||
|
|
||||||
enum columnId {
|
enum columnId {
|
||||||
@ -3,7 +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 type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable";
|
import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable";
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints";
|
import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints";
|
||||||
|
|
||||||
@ -5,7 +5,7 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||||
import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable";
|
import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable";
|
||||||
import { HorizontalPodAutoscalers } from "./hpa";
|
import { HorizontalPodAutoscalers } from "./list-view";
|
||||||
|
|
||||||
const horizontalPodAutoscalersRouteComponentInjectable = getInjectable({
|
const horizontalPodAutoscalersRouteComponentInjectable = getInjectable({
|
||||||
id: "horizontal-pod-autoscalers-route-component",
|
id: "horizontal-pod-autoscalers-route-component",
|
||||||
@ -3,44 +3,35 @@
|
|||||||
* 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 { KubeObject } from "../../../../../../common/k8s-api/kube-object";
|
import { KubeObject } from "../../../../../../common/k8s-api/kube-object";
|
||||||
import { parseKubeApi } from "../../../../../../common/k8s-api/kube-api-parse";
|
import { parseKubeApi } from "../../../../../../common/k8s-api/kube-api-parse";
|
||||||
import type { AsyncResult } from "@k8slens/utilities";
|
import type { AsyncResult } from "@k8slens/utilities";
|
||||||
import { getErrorMessage } from "../../../../../../common/utils/get-error-message";
|
import { getErrorMessage } from "../../../../../../common/utils/get-error-message";
|
||||||
import apiManagerInjectable from "../../../../../../common/k8s-api/api-manager/manager.injectable";
|
import apiKubeInjectable from "../../../../../k8s/api-kube.injectable";
|
||||||
import { waitUntilDefined } from "@k8slens/utilities";
|
|
||||||
|
|
||||||
export type CallForResource = (
|
export type CallForResource = (selfLink: string) => AsyncResult<KubeObject | undefined>;
|
||||||
selfLink: string
|
|
||||||
) => AsyncResult<KubeObject | undefined>;
|
|
||||||
|
|
||||||
const callForResourceInjectable = getInjectable({
|
const callForResourceInjectable = getInjectable({
|
||||||
id: "call-for-resource",
|
id: "call-for-resource",
|
||||||
|
|
||||||
instantiate: (di): CallForResource => {
|
instantiate: (di): CallForResource => {
|
||||||
const apiManager = di.inject(apiManagerInjectable);
|
const apiKube = di.inject(apiKubeInjectable);
|
||||||
|
|
||||||
return async (apiPath: string) => {
|
return async (apiPath: string) => {
|
||||||
const api = await waitUntilDefined(() => apiManager.getApi(apiPath));
|
|
||||||
|
|
||||||
const parsed = parseKubeApi(apiPath);
|
const parsed = parseKubeApi(apiPath);
|
||||||
|
|
||||||
if (!api || !parsed.name) {
|
if (!parsed.name) {
|
||||||
return { callWasSuccessful: false, error: "Invalid API path" };
|
return { callWasSuccessful: false, error: "Invalid API path" };
|
||||||
}
|
}
|
||||||
|
|
||||||
let resource: KubeObject | null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resource = await api.get({
|
return {
|
||||||
name: parsed.name,
|
callWasSuccessful: true,
|
||||||
namespace: parsed.namespace,
|
response: new KubeObject(await apiKube.get(apiPath)),
|
||||||
});
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { callWasSuccessful: false, error: getErrorMessage(e) };
|
return { callWasSuccessful: false, error: getErrorMessage(e) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { callWasSuccessful: true, response: resource || undefined };
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import type { CallForResource } from "./call-for-resource/call-for-resource.inje
|
|||||||
import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable";
|
import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable";
|
||||||
import { waitUntilDefined } from "@k8slens/utilities";
|
import { waitUntilDefined } from "@k8slens/utilities";
|
||||||
import editResourceTabStoreInjectable from "../store.injectable";
|
import editResourceTabStoreInjectable from "../store.injectable";
|
||||||
import type { EditResourceTabStore } from "../store";
|
import type { EditingResource, EditResourceTabStore } from "../store";
|
||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, observable, runInAction } from "mobx";
|
||||||
import type { KubeObject } from "../../../../../common/k8s-api/kube-object";
|
import type { KubeObject, RawKubeObject } from "../../../../../common/k8s-api/kube-object";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import type { CallForPatchResource } from "./call-for-patch-resource/call-for-patch-resource.injectable";
|
import type { CallForPatchResource } from "./call-for-patch-resource/call-for-patch-resource.injectable";
|
||||||
@ -19,18 +19,22 @@ import type { ShowNotification } from "../../../notifications";
|
|||||||
import showSuccessNotificationInjectable from "../../../notifications/show-success-notification.injectable";
|
import showSuccessNotificationInjectable from "../../../notifications/show-success-notification.injectable";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable";
|
import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable";
|
||||||
|
import { createKubeApiURL, parseKubeApi } from "../../../../../common/k8s-api/kube-api-parse";
|
||||||
|
|
||||||
const editResourceModelInjectable = getInjectable({
|
const editResourceModelInjectable = getInjectable({
|
||||||
id: "edit-resource-model",
|
id: "edit-resource-model",
|
||||||
|
|
||||||
instantiate: async (di, tabId: string) => {
|
instantiate: async (di, tabId: string) => {
|
||||||
|
const store = di.inject(editResourceTabStoreInjectable);
|
||||||
|
|
||||||
const model = new EditResourceModel({
|
const model = new EditResourceModel({
|
||||||
callForResource: di.inject(callForResourceInjectable),
|
callForResource: di.inject(callForResourceInjectable),
|
||||||
callForPatchResource: di.inject(callForPatchResourceInjectable),
|
callForPatchResource: di.inject(callForPatchResourceInjectable),
|
||||||
showSuccessNotification: di.inject(showSuccessNotificationInjectable),
|
showSuccessNotification: di.inject(showSuccessNotificationInjectable),
|
||||||
showErrorNotification: di.inject(showErrorNotificationInjectable),
|
showErrorNotification: di.inject(showErrorNotificationInjectable),
|
||||||
store: di.inject(editResourceTabStoreInjectable),
|
store,
|
||||||
tabId,
|
tabId,
|
||||||
|
waitForEditingResource: () => waitUntilDefined(() => store.getData(tabId)),
|
||||||
});
|
});
|
||||||
|
|
||||||
await model.load();
|
await model.load();
|
||||||
@ -48,19 +52,42 @@ export default editResourceModelInjectable;
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
callForResource: CallForResource;
|
callForResource: CallForResource;
|
||||||
callForPatchResource: CallForPatchResource;
|
callForPatchResource: CallForPatchResource;
|
||||||
|
waitForEditingResource: () => Promise<EditingResource>;
|
||||||
showSuccessNotification: ShowNotification;
|
showSuccessNotification: ShowNotification;
|
||||||
showErrorNotification: ShowNotification;
|
showErrorNotification: ShowNotification;
|
||||||
readonly store: EditResourceTabStore;
|
readonly store: EditResourceTabStore;
|
||||||
readonly tabId: string;
|
readonly tabId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditResourceModel {
|
function getEditSelfLinkFor(object: RawKubeObject): string {
|
||||||
constructor(private readonly dependencies: Dependencies) {
|
const lensVersionLabel = object.metadata.labels?.[EditResourceLabelName];
|
||||||
makeObservable(this);
|
|
||||||
|
if (lensVersionLabel) {
|
||||||
|
const { apiVersionWithGroup, ...parsedApi } = parseKubeApi(object.metadata.selfLink);
|
||||||
|
|
||||||
|
parsedApi.apiVersion = lensVersionLabel;
|
||||||
|
|
||||||
|
return createKubeApiURL({
|
||||||
|
...parsedApi,
|
||||||
|
apiVersion: `${parsedApi.apiGroup}/${parsedApi.apiVersion}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return object.metadata.selfLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label name that Lens uses to receive the desired api version
|
||||||
|
*/
|
||||||
|
export const EditResourceLabelName = "k8slens-edit-resource-version";
|
||||||
|
|
||||||
|
export class EditResourceModel {
|
||||||
|
constructor(protected readonly dependencies: Dependencies) {}
|
||||||
|
|
||||||
readonly configuration = {
|
readonly configuration = {
|
||||||
value: computed(() => this.editingResource.draft || this.editingResource.firstDraft || ""),
|
value: computed(
|
||||||
|
() => this.editingResource.draft || this.editingResource.firstDraft || "",
|
||||||
|
),
|
||||||
|
|
||||||
onChange: action((value: string) => {
|
onChange: action((value: string) => {
|
||||||
this.editingResource.draft = value;
|
this.editingResource.draft = value;
|
||||||
@ -100,27 +127,39 @@ export class EditResourceModel {
|
|||||||
return this.editingResource.resource;
|
return this.editingResource.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
load = async () => {
|
load = async (): Promise<void> => {
|
||||||
await waitUntilDefined(() => this.dependencies.store.getData(this.dependencies.tabId));
|
await this.dependencies.waitForEditingResource();
|
||||||
|
|
||||||
const result = await this.dependencies.callForResource(this.selfLink);
|
let result = await this.dependencies.callForResource(this.selfLink);
|
||||||
|
|
||||||
if (!result.callWasSuccessful) {
|
if (!result.callWasSuccessful) {
|
||||||
this.dependencies.showErrorNotification(
|
return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`);
|
||||||
`Loading resource failed: ${result.error}`,
|
}
|
||||||
);
|
|
||||||
|
|
||||||
|
if (result?.response?.metadata.labels?.[EditResourceLabelName]) {
|
||||||
|
const parsed = parseKubeApi(this.selfLink);
|
||||||
|
|
||||||
|
parsed.apiVersion = result.response.metadata.labels[EditResourceLabelName];
|
||||||
|
|
||||||
|
result = await this.dependencies.callForResource(createKubeApiURL(parsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.callWasSuccessful) {
|
||||||
|
return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resource = result.response;
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this._resource = resource;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this._resource = result.response;
|
this.editingResource.firstDraft = yaml.dump(resource.toPlainObject());
|
||||||
|
|
||||||
if (this._resource) {
|
|
||||||
this.editingResource.firstDraft = yaml.dump(
|
|
||||||
this._resource.toPlainObject(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,16 +177,16 @@ export class EditResourceModel {
|
|||||||
|
|
||||||
save = async () => {
|
save = async () => {
|
||||||
const currentValue = this.configuration.value.get();
|
const currentValue = this.configuration.value.get();
|
||||||
const currentVersion = yaml.load(currentValue);
|
const currentVersion = yaml.load(currentValue) as RawKubeObject;
|
||||||
const firstVersion = yaml.load(
|
const firstVersion = yaml.load(this.editingResource.firstDraft ?? currentValue);
|
||||||
this.editingResource.firstDraft ?? currentValue,
|
|
||||||
);
|
|
||||||
const patches = createPatch(firstVersion, currentVersion);
|
|
||||||
|
|
||||||
const result = await this.dependencies.callForPatchResource(
|
// Make sure we save this label so that we can use it in the future
|
||||||
this.resource,
|
currentVersion.metadata.labels ??= {};
|
||||||
patches,
|
currentVersion.metadata.labels[EditResourceLabelName] = currentVersion.apiVersion.split("/").pop();
|
||||||
);
|
|
||||||
|
const patches = createPatch(firstVersion, currentVersion);
|
||||||
|
const selfLink = getEditSelfLinkFor(currentVersion);
|
||||||
|
const result = await this.dependencies.callForPatchResource(this.resource, patches);
|
||||||
|
|
||||||
if (!result.callWasSuccessful) {
|
if (!result.callWasSuccessful) {
|
||||||
this.dependencies.showErrorNotification((
|
this.dependencies.showErrorNotification((
|
||||||
@ -158,23 +197,26 @@ export class EditResourceModel {
|
|||||||
</p>
|
</p>
|
||||||
));
|
));
|
||||||
|
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { kind, name } = result.response;
|
const { kind, name } = result.response;
|
||||||
|
|
||||||
this.dependencies.showSuccessNotification((
|
this.dependencies.showSuccessNotification(
|
||||||
<p>
|
<p>
|
||||||
{`${kind} `}
|
{kind}
|
||||||
|
{" "}
|
||||||
<b>{name}</b>
|
<b>{name}</b>
|
||||||
{" updated."}
|
{" updated."}
|
||||||
</p>
|
</p>,
|
||||||
));
|
);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.editingResource.firstDraft = currentValue;
|
this.editingResource.firstDraft = yaml.dump(currentVersion);
|
||||||
|
this.editingResource.resource = selfLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.response.toString();
|
// NOTE: This is required for `saveAndClose` to work correctly
|
||||||
|
return [];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,12 +56,14 @@ const NonInjectedEditResource = observer(({
|
|||||||
<span>Namespace:</span>
|
<span>Namespace:</span>
|
||||||
<Badge label={model.namespace} />
|
<Badge label={model.namespace} />
|
||||||
</div>
|
</div>
|
||||||
)} />
|
)}
|
||||||
|
/>
|
||||||
<EditorPanel
|
<EditorPanel
|
||||||
tabId={tabId}
|
tabId={tabId}
|
||||||
value={model.configuration.value.get()}
|
value={model.configuration.value.get()}
|
||||||
onChange={model.configuration.onChange}
|
onChange={model.configuration.onChange}
|
||||||
onError={model.configuration.error.onChange} />
|
onError={model.configuration.error.onChange}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token";
|
import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token";
|
||||||
import { HpaDetails } from "../../../+config-horizontal-pod-autoscalers";
|
import { HorizontalPodAutoscalerDetails } from "../../../+config-horizontal-pod-autoscalers";
|
||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version";
|
import { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version";
|
||||||
import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable";
|
import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable";
|
||||||
@ -16,7 +16,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({
|
|||||||
const kubeObject = di.inject(currentKubeObjectInDetailsInjectable);
|
const kubeObject = di.inject(currentKubeObjectInDetailsInjectable);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Component: HpaDetails,
|
Component: HorizontalPodAutoscalerDetails,
|
||||||
enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)),
|
enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)),
|
||||||
orderNumber: 10,
|
orderNumber: 10,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -246,12 +246,10 @@ class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Depend
|
|||||||
|
|
||||||
this.dispose.push(
|
this.dispose.push(
|
||||||
reaction(() => this.model, this.onModelChange),
|
reaction(() => this.model, this.onModelChange),
|
||||||
reaction(() => this.theme, theme => {
|
reaction(() => this.theme, editor.setTheme),
|
||||||
if (theme) {
|
reaction(() => this.props.value, value => this.setValue(value), {
|
||||||
editor.setTheme(theme);
|
fireImmediately: true,
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
reaction(() => this.props.value, value => this.setValue(value)),
|
|
||||||
reaction(() => this.options, opts => this.editor.updateOptions(opts)),
|
reaction(() => this.options, opts => this.editor.updateOptions(opts)),
|
||||||
|
|
||||||
() => onDidLayoutChangeDisposer.dispose(),
|
() => onDidLayoutChangeDisposer.dispose(),
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 100,
|
"printWidth": 120,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
|
|||||||
@ -13,9 +13,7 @@ const startApplicationInjectable = getInjectable({
|
|||||||
|
|
||||||
instantiate: (di): StartApplication => {
|
instantiate: (di): StartApplication => {
|
||||||
const runManyAsync = runManyFor(di);
|
const runManyAsync = runManyFor(di);
|
||||||
const beforeApplicationIsLoading = runManyAsync(
|
const beforeApplicationIsLoading = runManyAsync(timeSlots.beforeApplicationIsLoadingInjectionToken);
|
||||||
timeSlots.beforeApplicationIsLoadingInjectionToken,
|
|
||||||
);
|
|
||||||
const onLoadOfApplication = runManyAsync(timeSlots.onLoadOfApplicationInjectionToken);
|
const onLoadOfApplication = runManyAsync(timeSlots.onLoadOfApplicationInjectionToken);
|
||||||
const afterApplicationIsLoaded = runManyAsync(timeSlots.afterApplicationIsLoadedInjectionToken);
|
const afterApplicationIsLoaded = runManyAsync(timeSlots.afterApplicationIsLoadedInjectionToken);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { DiContainer, getInjectable, instantiationDecoratorToken, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
DiContainer,
|
|
||||||
getInjectable,
|
|
||||||
instantiationDecoratorToken,
|
|
||||||
lifecycleEnum,
|
|
||||||
} from "@ogre-tools/injectable";
|
|
||||||
import { startApplicationInjectionToken } from "@k8slens/application";
|
import { startApplicationInjectionToken } from "@k8slens/application";
|
||||||
import whenAppIsReadyInjectable from "./when-app-is-ready.injectable";
|
import whenAppIsReadyInjectable from "./when-app-is-ready.injectable";
|
||||||
import { beforeAnythingInjectionToken, beforeElectronIsReadyInjectionToken } from "./time-slots";
|
import { beforeAnythingInjectionToken, beforeElectronIsReadyInjectionToken } from "./time-slots";
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable";
|
import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable";
|
||||||
import { registerFeature } from "@k8slens/feature-core";
|
import { registerFeature } from "@k8slens/feature-core";
|
||||||
import { applicationFeatureForElectronMain } from "./feature";
|
import { applicationFeatureForElectronMain } from "./feature";
|
||||||
import {
|
import { beforeApplicationIsLoadingInjectionToken, startApplicationInjectionToken } from "@k8slens/application";
|
||||||
beforeApplicationIsLoadingInjectionToken,
|
|
||||||
startApplicationInjectionToken,
|
|
||||||
} from "@k8slens/application";
|
|
||||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||||
import whenAppIsReadyInjectable from "./start-application/when-app-is-ready.injectable";
|
import whenAppIsReadyInjectable from "./start-application/when-app-is-ready.injectable";
|
||||||
import * as timeSlots from "./start-application/time-slots";
|
import * as timeSlots from "./start-application/time-slots";
|
||||||
|
|||||||
@ -2,9 +2,7 @@ import type { DiContainer } from "@ogre-tools/injectable";
|
|||||||
import type { Feature } from "./feature";
|
import type { Feature } from "./feature";
|
||||||
import { featureContextMapInjectable } from "./feature-context-map-injectable";
|
import { featureContextMapInjectable } from "./feature-context-map-injectable";
|
||||||
|
|
||||||
const getDependingFeaturesFor = (
|
const getDependingFeaturesFor = (featureContextMap: Map<Feature, { dependedBy: Map<Feature, number> }>) => {
|
||||||
featureContextMap: Map<Feature, { dependedBy: Map<Feature, number> }>,
|
|
||||||
) => {
|
|
||||||
const getDependingFeaturesForRecursion = (feature: Feature, atRoot = true): string[] => {
|
const getDependingFeaturesForRecursion = (feature: Feature, atRoot = true): string[] => {
|
||||||
const context = featureContextMap.get(feature);
|
const context = featureContextMap.get(feature);
|
||||||
|
|
||||||
@ -36,11 +34,9 @@ const deregisterFeatureRecursed = (di: DiContainer, feature: Feature, dependedBy
|
|||||||
const dependingFeatures = getDependingFeatures(feature);
|
const dependingFeatures = getDependingFeatures(feature);
|
||||||
|
|
||||||
if (!dependedBy && dependingFeatures.length) {
|
if (!dependedBy && dependingFeatures.length) {
|
||||||
throw new Error(
|
const names = dependingFeatures.join(", ");
|
||||||
`Tried to deregister Feature "${
|
|
||||||
feature.id
|
throw new Error(`Tried to deregister Feature "${feature.id}", but it is the dependency of Features "${names}"`);
|
||||||
}", but it is the dependency of Features "${dependingFeatures.join(", ")}"`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dependedBy) {
|
if (dependedBy) {
|
||||||
|
|||||||
@ -59,9 +59,7 @@ describe("feature-dependencies", () => {
|
|||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
deregisterFeature(di, someDependencyFeature);
|
deregisterFeature(di, someDependencyFeature);
|
||||||
}).toThrow(
|
}).toThrow('Tried to deregister feature "some-dependency-feature", but it was not registered.');
|
||||||
'Tried to deregister feature "some-dependency-feature", but it was not registered.',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("given the parent Feature is deregistered, when injecting an injectable from the dependency Feature, throws", () => {
|
it("given the parent Feature is deregistered, when injecting an injectable from the dependency Feature, throws", () => {
|
||||||
@ -104,9 +102,7 @@ describe("feature-dependencies", () => {
|
|||||||
it("when the first Feature is deregistered, throws", () => {
|
it("when the first Feature is deregistered, throws", () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
deregisterFeature(di, someFeature1);
|
deregisterFeature(di, someFeature1);
|
||||||
}).toThrow(
|
}).toThrow('Tried to deregister Feature "some-feature-1", but it is the dependency of Features "some-feature-2"');
|
||||||
'Tried to deregister Feature "some-feature-1", but it is the dependency of Features "some-feature-2"',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("given the second Feature is deregistered, when injecting an injectable from the first Feature, still does so", () => {
|
it("given the second Feature is deregistered, when injecting an injectable from the first Feature, still does so", () => {
|
||||||
@ -180,9 +176,7 @@ describe("feature-dependencies", () => {
|
|||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
di.inject(someInjectableInDependencyFeature);
|
di.inject(someInjectableInDependencyFeature);
|
||||||
}).toThrow(
|
}).toThrow('Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".');
|
||||||
'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,9 +250,7 @@ describe("feature-dependencies", () => {
|
|||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
di.inject(someInjectableInDependencyFeature);
|
di.inject(someInjectableInDependencyFeature);
|
||||||
}).toThrow(
|
}).toThrow('Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".');
|
||||||
'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { Feature } from "./feature";
|
import type { Feature } from "./feature";
|
||||||
import {
|
import { featureContextMapInjectable, featureContextMapInjectionToken } from "./feature-context-map-injectable";
|
||||||
featureContextMapInjectable,
|
|
||||||
featureContextMapInjectionToken,
|
|
||||||
} from "./feature-context-map-injectable";
|
|
||||||
|
|
||||||
const createFeatureContext = (feature: Feature, di: DiContainer) => {
|
const createFeatureContext = (feature: Feature, di: DiContainer) => {
|
||||||
const featureContextInjectable = getInjectable({
|
const featureContextInjectable = getInjectable({
|
||||||
@ -58,10 +55,9 @@ const registerFeatureRecursed = (di: DiContainer, feature: Feature, dependedBy?:
|
|||||||
|
|
||||||
if (dependedBy) {
|
if (dependedBy) {
|
||||||
const oldNumberOfDependents = featureContext.dependedBy.get(dependedBy) || 0;
|
const oldNumberOfDependents = featureContext.dependedBy.get(dependedBy) || 0;
|
||||||
|
const newNumberOfDependents = oldNumberOfDependents + 1;
|
||||||
|
|
||||||
const newNumberOfDependants = oldNumberOfDependents + 1;
|
featureContext.dependedBy.set(dependedBy, newNumberOfDependents);
|
||||||
|
|
||||||
featureContext.dependedBy.set(dependedBy, newNumberOfDependants);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingFeatureContext) {
|
if (!existingFeatureContext) {
|
||||||
|
|||||||
@ -21,15 +21,9 @@ export {
|
|||||||
getMessageChannelListenerInjectable,
|
getMessageChannelListenerInjectable,
|
||||||
} from "./message/message-channel-listener-injection-token";
|
} from "./message/message-channel-listener-injection-token";
|
||||||
|
|
||||||
export type {
|
export type { RequestChannel, RequestChannelHandler } from "./request/request-channel-listener-injection-token";
|
||||||
RequestChannel,
|
|
||||||
RequestChannelHandler,
|
|
||||||
} from "./request/request-channel-listener-injection-token";
|
|
||||||
|
|
||||||
export type {
|
export type { RequestFromChannel, ChannelRequester } from "./request/request-from-channel-injection-token";
|
||||||
RequestFromChannel,
|
|
||||||
ChannelRequester,
|
|
||||||
} from "./request/request-from-channel-injection-token";
|
|
||||||
|
|
||||||
export type { EnlistMessageChannelListener } from "./message/enlist-message-channel-listener-injection-token";
|
export type { EnlistMessageChannelListener } from "./message/enlist-message-channel-listener-injection-token";
|
||||||
export { enlistMessageChannelListenerInjectionToken } from "./message/enlist-message-channel-listener-injection-token";
|
export { enlistMessageChannelListenerInjectionToken } from "./message/enlist-message-channel-listener-injection-token";
|
||||||
|
|||||||
@ -21,9 +21,7 @@ export const listeningOfChannelsInjectionToken = getInjectionToken<ListeningOfCh
|
|||||||
id: "listening-of-channels-injection-token",
|
id: "listening-of-channels-injection-token",
|
||||||
});
|
});
|
||||||
|
|
||||||
const listening = <
|
const listening = <T extends { id: string; channel: MessageChannel<any> | RequestChannel<any, any> }>(
|
||||||
T extends { id: string; channel: MessageChannel<any> | RequestChannel<any, any> },
|
|
||||||
>(
|
|
||||||
channelListeners: IComputedValue<T[]>,
|
channelListeners: IComputedValue<T[]>,
|
||||||
enlistChannelListener: (listener: T) => () => void,
|
enlistChannelListener: (listener: T) => () => void,
|
||||||
getId: (listener: T) => string,
|
getId: (listener: T) => string,
|
||||||
@ -33,9 +31,7 @@ const listening = <
|
|||||||
const reactionDisposer = reaction(
|
const reactionDisposer = reaction(
|
||||||
() => channelListeners.get(),
|
() => channelListeners.get(),
|
||||||
(newValues, oldValues = []) => {
|
(newValues, oldValues = []) => {
|
||||||
const addedListeners = newValues.filter(
|
const addedListeners = newValues.filter((newValue) => !oldValues.some((oldValue) => oldValue.id === newValue.id));
|
||||||
(newValue) => !oldValues.some((oldValue) => oldValue.id === newValue.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
const removedListeners = oldValues.filter(
|
const removedListeners = oldValues.filter(
|
||||||
(oldValue) => !newValues.some((newValue) => newValue.id === oldValue.id),
|
(oldValue) => !newValues.some((newValue) => newValue.id === oldValue.id),
|
||||||
@ -45,9 +41,7 @@ const listening = <
|
|||||||
const id = getId(listener);
|
const id = getId(listener);
|
||||||
|
|
||||||
if (listenerDisposers.has(id)) {
|
if (listenerDisposers.has(id)) {
|
||||||
throw new Error(
|
throw new Error(`Tried to add listener for channel "${listener.channel.id}" but listener already exists.`);
|
||||||
`Tried to add listener for channel "${listener.channel.id}" but listener already exists.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const disposer = enlistChannelListener(listener);
|
const disposer = enlistChannelListener(listener);
|
||||||
|
|||||||
@ -1,16 +1,10 @@
|
|||||||
import type { Disposer } from "@k8slens/utilities";
|
import type { Disposer } from "@k8slens/utilities";
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
import type {
|
import type { MessageChannel, MessageChannelListener } from "./message-channel-listener-injection-token";
|
||||||
MessageChannel,
|
|
||||||
MessageChannelListener,
|
|
||||||
} from "./message-channel-listener-injection-token";
|
|
||||||
|
|
||||||
export type EnlistMessageChannelListener = <T>(
|
export type EnlistMessageChannelListener = <T>(listener: MessageChannelListener<MessageChannel<T>>) => Disposer;
|
||||||
listener: MessageChannelListener<MessageChannel<T>>,
|
|
||||||
) => Disposer;
|
|
||||||
|
|
||||||
export const enlistMessageChannelListenerInjectionToken =
|
export const enlistMessageChannelListenerInjectionToken = getInjectionToken<EnlistMessageChannelListener>({
|
||||||
getInjectionToken<EnlistMessageChannelListener>({
|
|
||||||
id: "listening-to-a-message-channel",
|
id: "listening-to-a-message-channel",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -18,9 +18,7 @@ export interface MessageChannelListener<Channel> {
|
|||||||
handler: MessageChannelHandler<Channel>;
|
handler: MessageChannelHandler<Channel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const messageChannelListenerInjectionToken = getInjectionToken<
|
export const messageChannelListenerInjectionToken = getInjectionToken<MessageChannelListener<MessageChannel<unknown>>>({
|
||||||
MessageChannelListener<MessageChannel<unknown>>
|
|
||||||
>({
|
|
||||||
id: "message-channel-listener",
|
id: "message-channel-listener",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -31,10 +29,7 @@ export interface GetMessageChannelListenerInfo<Channel extends MessageChannel<Me
|
|||||||
causesSideEffects?: boolean;
|
causesSideEffects?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMessageChannelListenerInjectable = <
|
export const getMessageChannelListenerInjectable = <Channel extends MessageChannel<Message>, Message>(
|
||||||
Channel extends MessageChannel<Message>,
|
|
||||||
Message,
|
|
||||||
>(
|
|
||||||
info: GetMessageChannelListenerInfo<Channel, Message>,
|
info: GetMessageChannelListenerInfo<Channel, Message>,
|
||||||
) =>
|
) =>
|
||||||
getInjectable({
|
getInjectable({
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import type { Disposer } from "@k8slens/utilities/index";
|
import type { Disposer } from "@k8slens/utilities/index";
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
import type {
|
import type { RequestChannel, RequestChannelListener } from "./request-channel-listener-injection-token";
|
||||||
RequestChannel,
|
|
||||||
RequestChannelListener,
|
|
||||||
} from "./request-channel-listener-injection-token";
|
|
||||||
|
|
||||||
export type EnlistRequestChannelListener = <Request, Response>(
|
export type EnlistRequestChannelListener = <Request, Response>(
|
||||||
listener: RequestChannelListener<RequestChannel<Request, Response>>,
|
listener: RequestChannelListener<RequestChannel<Request, Response>>,
|
||||||
) => Disposer;
|
) => Disposer;
|
||||||
|
|
||||||
export const enlistRequestChannelListenerInjectionToken =
|
export const enlistRequestChannelListenerInjectionToken = getInjectionToken<EnlistRequestChannelListener>({
|
||||||
getInjectionToken<EnlistRequestChannelListener>({
|
|
||||||
id: "listening-to-a-request-channel",
|
id: "listening-to-a-request-channel",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import type { RequestChannel } from "./request-channel-listener-injection-token";
|
import type { RequestChannel } from "./request-channel-listener-injection-token";
|
||||||
|
|
||||||
export const getRequestChannel = <Request, Response>(
|
export const getRequestChannel = <Request, Response>(id: string): RequestChannel<Request, Response> => ({
|
||||||
id: string,
|
|
||||||
): RequestChannel<Request, Response> => ({
|
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,10 +7,7 @@ export interface RequestChannel<Request, Response> {
|
|||||||
_responseSignature?: Response;
|
_responseSignature?: Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestChannelHandler<Channel> = Channel extends RequestChannel<
|
export type RequestChannelHandler<Channel> = Channel extends RequestChannel<infer Request, infer Response>
|
||||||
infer Request,
|
|
||||||
infer Response
|
|
||||||
>
|
|
||||||
? (req: Request) => Promise<Response> | Response
|
? (req: Request) => Promise<Response> | Response
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
|||||||
@ -2,17 +2,11 @@ import { getInjectionToken } from "@ogre-tools/injectable";
|
|||||||
import type { RequestChannel } from "./request-channel-listener-injection-token";
|
import type { RequestChannel } from "./request-channel-listener-injection-token";
|
||||||
|
|
||||||
export interface RequestFromChannel {
|
export interface RequestFromChannel {
|
||||||
<Request, Response>(
|
<Request, Response>(channel: RequestChannel<Request, Response>, request: Request): Promise<Response>;
|
||||||
channel: RequestChannel<Request, Response>,
|
|
||||||
request: Request,
|
|
||||||
): Promise<Response>;
|
|
||||||
<Response>(channel: RequestChannel<void, Response>): Promise<Response>;
|
<Response>(channel: RequestChannel<void, Response>): Promise<Response>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChannelRequester<Channel> = Channel extends RequestChannel<
|
export type ChannelRequester<Channel> = Channel extends RequestChannel<infer Request, infer Response>
|
||||||
infer Request,
|
|
||||||
infer Response
|
|
||||||
>
|
|
||||||
? (req: Request) => Promise<Awaited<Response>>
|
? (req: Request) => Promise<Awaited<Response>>
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
|||||||
@ -121,9 +121,7 @@ describe("listening-of-requests", () => {
|
|||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
di.register(someConflictingListenerInjectable);
|
di.register(someConflictingListenerInjectable);
|
||||||
});
|
});
|
||||||
}).toThrow(
|
}).toThrow('Tried to add listener for channel "some-channel-id" but listener already exists.');
|
||||||
'Tried to add listener for channel "some-channel-id" but listener already exists.',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when another listener gets registered", () => {
|
describe("when another listener gets registered", () => {
|
||||||
|
|||||||
@ -3,7 +3,4 @@ export {
|
|||||||
computedChannelObserverInjectionToken,
|
computedChannelObserverInjectionToken,
|
||||||
} from "./src/computed-channel/computed-channel.injectable";
|
} from "./src/computed-channel/computed-channel.injectable";
|
||||||
|
|
||||||
export type {
|
export type { ChannelObserver, ComputedChannelFactory } from "./src/computed-channel/computed-channel.injectable";
|
||||||
ChannelObserver,
|
|
||||||
ComputedChannelFactory,
|
|
||||||
} from "./src/computed-channel/computed-channel.injectable";
|
|
||||||
|
|||||||
@ -1,23 +1,13 @@
|
|||||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
import {
|
import { computed, IComputedValue, observable, onBecomeObserved, onBecomeUnobserved, runInAction } from "mobx";
|
||||||
computed,
|
|
||||||
IComputedValue,
|
|
||||||
observable,
|
|
||||||
onBecomeObserved,
|
|
||||||
onBecomeUnobserved,
|
|
||||||
runInAction,
|
|
||||||
} from "mobx";
|
|
||||||
|
|
||||||
import type { MessageChannel } from "@k8slens/messaging";
|
import type { MessageChannel } from "@k8slens/messaging";
|
||||||
import { getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
import { getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
||||||
import { sendMessageToChannelInjectionToken } from "@k8slens/messaging";
|
import { sendMessageToChannelInjectionToken } from "@k8slens/messaging";
|
||||||
import { computedChannelAdministrationChannel } from "./computed-channel-administration-channel.injectable";
|
import { computedChannelAdministrationChannel } from "./computed-channel-administration-channel.injectable";
|
||||||
|
|
||||||
export type ComputedChannelFactory = <T>(
|
export type ComputedChannelFactory = <T>(channel: MessageChannel<T>, pendingValue: T) => IComputedValue<T>;
|
||||||
channel: MessageChannel<T>,
|
|
||||||
pendingValue: T,
|
|
||||||
) => IComputedValue<T>;
|
|
||||||
|
|
||||||
export const computedChannelInjectionToken = getInjectionToken<ComputedChannelFactory>({
|
export const computedChannelInjectionToken = getInjectionToken<ComputedChannelFactory>({
|
||||||
id: "computed-channel-injection-token",
|
id: "computed-channel-injection-token",
|
||||||
|
|||||||
@ -3,23 +3,13 @@ import { act } from "@testing-library/react";
|
|||||||
import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable";
|
import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable";
|
||||||
import { getMessageBridgeFake, MessageBridgeFake } from "@k8slens/messaging-fake-bridge";
|
import { getMessageBridgeFake, MessageBridgeFake } from "@k8slens/messaging-fake-bridge";
|
||||||
import { startApplicationInjectionToken } from "@k8slens/application";
|
import { startApplicationInjectionToken } from "@k8slens/application";
|
||||||
import {
|
import { computed, IComputedValue, IObservableValue, observable, reaction, runInAction } from "mobx";
|
||||||
computed,
|
|
||||||
IComputedValue,
|
|
||||||
IObservableValue,
|
|
||||||
observable,
|
|
||||||
reaction,
|
|
||||||
runInAction,
|
|
||||||
} from "mobx";
|
|
||||||
import type { MessageChannel } from "@k8slens/messaging";
|
import type { MessageChannel } from "@k8slens/messaging";
|
||||||
import { getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
import { getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
||||||
import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
|
import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
|
||||||
import { registerFeature } from "@k8slens/feature-core";
|
import { registerFeature } from "@k8slens/feature-core";
|
||||||
import { testUtils } from "@k8slens/messaging";
|
import { testUtils } from "@k8slens/messaging";
|
||||||
import {
|
import { computedChannelInjectionToken, computedChannelObserverInjectionToken } from "./computed-channel.injectable";
|
||||||
computedChannelInjectionToken,
|
|
||||||
computedChannelObserverInjectionToken,
|
|
||||||
} from "./computed-channel.injectable";
|
|
||||||
import { runWithThrownMobxReactions, renderFor } from "@k8slens/test-utils";
|
import { runWithThrownMobxReactions, renderFor } from "@k8slens/test-utils";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import {
|
import {
|
||||||
@ -36,9 +26,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
));
|
));
|
||||||
|
|
||||||
[{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) =>
|
[{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) =>
|
||||||
describe(`computed-channel, given running message bridge fake as ${
|
describe(`computed-channel, given running message bridge fake as ${scenarioIsAsync ? "async" : "sync"}`, () => {
|
||||||
scenarioIsAsync ? "async" : "sync"
|
|
||||||
}`, () => {
|
|
||||||
describe("given multiple dis and a message channel and a channel observer and application has started", () => {
|
describe("given multiple dis and a message channel and a channel observer and application has started", () => {
|
||||||
let di1: DiContainer;
|
let di1: DiContainer;
|
||||||
let di2: DiContainer;
|
let di2: DiContainer;
|
||||||
@ -87,10 +75,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
messageBridgeFake.setAsync(scenarioIsAsync);
|
messageBridgeFake.setAsync(scenarioIsAsync);
|
||||||
messageBridgeFake.involve(di1, di2);
|
messageBridgeFake.involve(di1, di2);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([di1.inject(startApplicationInjectionToken)(), di2.inject(startApplicationInjectionToken)()]);
|
||||||
di1.inject(startApplicationInjectionToken)(),
|
|
||||||
di2.inject(startApplicationInjectionToken)(),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given a channel observer and matching computed channel for the channel in di-2", () => {
|
describe("given a channel observer and matching computed channel for the channel in di-2", () => {
|
||||||
@ -175,9 +160,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
expect(observedValue).toBe("some-pending-value");
|
expect(observedValue).toBe("some-pending-value");
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when admin messages are propagated" : "immediately";
|
||||||
? "when admin messages are propagated"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
@ -196,9 +179,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when returning value-messages propagate" : "immediately";
|
||||||
? "when returning value-messages propagate"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
@ -227,9 +208,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when value-messages propagate" : "immediately";
|
||||||
? "when value-messages propagate"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
@ -258,9 +237,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
stopObserving();
|
stopObserving();
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when admin-messages propagate" : "immediately";
|
||||||
? "when admin-messages propagate"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
@ -317,9 +294,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
expect(observedValue).toBe("some-pending-value");
|
expect(observedValue).toBe("some-pending-value");
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when admin messages propagate" : "immediately";
|
||||||
? "when admin messages propagate"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
@ -345,9 +320,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
expect(observedValue).toBe("some-pending-value");
|
expect(observedValue).toBe("some-pending-value");
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioTitle = scenarioIsAsync
|
const scenarioTitle = scenarioIsAsync ? "when value-messages propagate back" : "immediately";
|
||||||
? "when value-messages propagate back"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioTitle, () => {
|
describe(scenarioTitle, () => {
|
||||||
@ -466,9 +439,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue
|
|||||||
expect(nonReactiveValue).toBe("some-initial-value");
|
expect(nonReactiveValue).toBe("some-initial-value");
|
||||||
});
|
});
|
||||||
|
|
||||||
const scenarioName = scenarioIsAsync
|
const scenarioName = scenarioIsAsync ? "when messages would be propagated" : "immediately";
|
||||||
? "when messages would be propagated"
|
|
||||||
: "immediately";
|
|
||||||
|
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
describe(scenarioName, () => {
|
describe(scenarioName, () => {
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import ipcMainInjectable from "../ipc-main/ipc-main.injectable";
|
import ipcMainInjectable from "../ipc-main/ipc-main.injectable";
|
||||||
import type { IpcMain, IpcMainEvent } from "electron";
|
import type { IpcMain, IpcMainEvent } from "electron";
|
||||||
import {
|
import { EnlistMessageChannelListener, enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
||||||
EnlistMessageChannelListener,
|
|
||||||
enlistMessageChannelListenerInjectionToken,
|
|
||||||
} from "@k8slens/messaging";
|
|
||||||
import { createContainer } from "@ogre-tools/injectable";
|
import { createContainer } from "@ogre-tools/injectable";
|
||||||
import { registerFeature } from "@k8slens/feature-core";
|
import { registerFeature } from "@k8slens/feature-core";
|
||||||
import { messagingFeatureForMain } from "../feature";
|
import { messagingFeatureForMain } from "../feature";
|
||||||
|
|||||||
@ -1,19 +1,13 @@
|
|||||||
/* c8 ignore start */
|
/* c8 ignore start */
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import {
|
import { RequestChannel, RequestFromChannel, requestFromChannelInjectionToken } from "@k8slens/messaging";
|
||||||
RequestChannel,
|
|
||||||
RequestFromChannel,
|
|
||||||
requestFromChannelInjectionToken,
|
|
||||||
} from "@k8slens/messaging";
|
|
||||||
|
|
||||||
const requestFromChannelInjectable = getInjectable({
|
const requestFromChannelInjectable = getInjectable({
|
||||||
id: "request-from-channel",
|
id: "request-from-channel",
|
||||||
|
|
||||||
instantiate: () =>
|
instantiate: () =>
|
||||||
((channel: RequestChannel<any, any>) => {
|
((channel: RequestChannel<any, any>) => {
|
||||||
throw new Error(
|
throw new Error(`Tried to request from channel "${channel.id}" but requesting in "main" it's not supported yet`);
|
||||||
`Tried to request from channel "${channel.id}" but requesting in "main" it's not supported yet`,
|
|
||||||
);
|
|
||||||
}) as unknown as RequestFromChannel,
|
}) as unknown as RequestFromChannel,
|
||||||
|
|
||||||
injectionToken: requestFromChannelInjectionToken,
|
injectionToken: requestFromChannelInjectionToken,
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import { getMessageChannel, getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
import { getMessageChannel, getMessageChannelListenerInjectable } from "@k8slens/messaging";
|
||||||
import frameIdsInjectable from "./frameIds.injectable";
|
import frameIdsInjectable from "./frameIds.injectable";
|
||||||
|
|
||||||
const frameCommunicationAdminChannel = getMessageChannel<undefined>(
|
const frameCommunicationAdminChannel = getMessageChannel<undefined>("frame-communication-admin-channel");
|
||||||
"frame-communication-admin-channel",
|
|
||||||
);
|
|
||||||
|
|
||||||
const allowCommunicationListenerInjectable = getMessageChannelListenerInjectable({
|
const allowCommunicationListenerInjectable = getMessageChannelListenerInjectable({
|
||||||
id: "allow-communication",
|
id: "allow-communication",
|
||||||
|
|||||||
@ -2,9 +2,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { onLoadOfApplicationInjectionToken } from "@k8slens/application";
|
import { onLoadOfApplicationInjectionToken } from "@k8slens/application";
|
||||||
import { getMessageChannel, sendMessageToChannelInjectionToken } from "@k8slens/messaging";
|
import { getMessageChannel, sendMessageToChannelInjectionToken } from "@k8slens/messaging";
|
||||||
|
|
||||||
export const frameCommunicationAdminChannel = getMessageChannel<undefined>(
|
export const frameCommunicationAdminChannel = getMessageChannel<undefined>("frame-communication-admin-channel");
|
||||||
"frame-communication-admin-channel",
|
|
||||||
);
|
|
||||||
|
|
||||||
const allowCommunicationToIframeInjectable = getInjectable({
|
const allowCommunicationToIframeInjectable = getInjectable({
|
||||||
id: "allow-communication-to-iframe-injectable",
|
id: "allow-communication-to-iframe-injectable",
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import type { IpcRendererEvent, IpcRenderer } from "electron";
|
import type { IpcRendererEvent, IpcRenderer } from "electron";
|
||||||
import ipcRendererInjectable from "../ipc/ipc-renderer.injectable";
|
import ipcRendererInjectable from "../ipc/ipc-renderer.injectable";
|
||||||
import {
|
import { EnlistMessageChannelListener, enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
||||||
EnlistMessageChannelListener,
|
|
||||||
enlistMessageChannelListenerInjectionToken,
|
|
||||||
} from "@k8slens/messaging";
|
|
||||||
import { createContainer } from "@ogre-tools/injectable";
|
import { createContainer } from "@ogre-tools/injectable";
|
||||||
import { registerFeature } from "@k8slens/feature-core";
|
import { registerFeature } from "@k8slens/feature-core";
|
||||||
import { messagingFeatureForRenderer } from "../feature";
|
import { messagingFeatureForRenderer } from "../feature";
|
||||||
|
|||||||
@ -32,9 +32,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
[{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) =>
|
[{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) =>
|
||||||
describe(`get-message-bridge-fake, given running as ${
|
describe(`get-message-bridge-fake, given running as ${scenarioIsAsync ? "async" : "sync"}`, () => {
|
||||||
scenarioIsAsync ? "async" : "sync"
|
|
||||||
}`, () => {
|
|
||||||
let messageBridgeFake: any;
|
let messageBridgeFake: any;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -135,9 +133,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
|
|
||||||
describe("given a message is sent in di-1", () => {
|
describe("given a message is sent in di-1", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const sendMessageToChannelFromDi1 = someDi1.inject(
|
const sendMessageToChannelFromDi1 = someDi1.inject(sendMessageToChannelInjectionToken);
|
||||||
sendMessageToChannelInjectionToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
sendMessageToChannelFromDi1(someMessageChannel, "some-message");
|
sendMessageToChannelFromDi1(someMessageChannel, "some-message");
|
||||||
});
|
});
|
||||||
@ -161,13 +157,10 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("the response gets handled in di-1", () => {
|
it("the response gets handled in di-1", () => {
|
||||||
expect(someHandler1MockInDi1).toHaveBeenCalledWith(
|
expect(someHandler1MockInDi1).toHaveBeenCalledWith("some-response-to: some-message", {
|
||||||
"some-response-to: some-message",
|
|
||||||
{
|
|
||||||
frameId: 42,
|
frameId: 42,
|
||||||
processId: 42,
|
processId: 42,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
scenarioIsAsync &&
|
scenarioIsAsync &&
|
||||||
@ -191,13 +184,10 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("the response gets handled in di-1", () => {
|
it("the response gets handled in di-1", () => {
|
||||||
expect(someHandler1MockInDi1).toHaveBeenCalledWith(
|
expect(someHandler1MockInDi1).toHaveBeenCalledWith("some-response-to: some-message", {
|
||||||
"some-response-to: some-message",
|
|
||||||
{
|
|
||||||
frameId: 42,
|
frameId: 42,
|
||||||
processId: 42,
|
processId: 42,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -375,9 +365,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
|
|
||||||
const requestFromChannelFromDi2 = someDi2.inject(requestFromChannelInjectionToken);
|
const requestFromChannelFromDi2 = someDi2.inject(requestFromChannelInjectionToken);
|
||||||
|
|
||||||
return expect(() =>
|
return expect(() => requestFromChannelFromDi2(someRequestChannel, "irrelevant")).rejects.toThrow(
|
||||||
requestFromChannelFromDi2(someRequestChannel, "irrelevant"),
|
|
||||||
).rejects.toThrow(
|
|
||||||
'Tried to make a request but multiple listeners were discovered for channel "some-request-channel" in multiple DIs.',
|
'Tried to make a request but multiple listeners were discovered for channel "some-request-channel" in multiple DIs.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -385,9 +373,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = {
|
|||||||
it("when requesting from channel without listener, throws", () => {
|
it("when requesting from channel without listener, throws", () => {
|
||||||
const requestFromChannel = someDi1.inject(requestFromChannelInjectionToken);
|
const requestFromChannel = someDi1.inject(requestFromChannelInjectionToken);
|
||||||
|
|
||||||
return expect(() =>
|
return expect(() => requestFromChannel(someRequestChannelWithoutListeners, "irrelevant")).rejects.toThrow(
|
||||||
requestFromChannel(someRequestChannelWithoutListeners, "irrelevant"),
|
|
||||||
).rejects.toThrow(
|
|
||||||
'Tried to make a request but no listeners for channel "some-request-channel-without-listeners" was discovered in any DIs',
|
'Tried to make a request but no listeners for channel "some-request-channel-without-listeners" was discovered in any DIs',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
|||||||
export type MessageBridgeFake = {
|
export type MessageBridgeFake = {
|
||||||
involve: (...dis: DiContainer[]) => void;
|
involve: (...dis: DiContainer[]) => void;
|
||||||
messagePropagation: () => Promise<void>;
|
messagePropagation: () => Promise<void>;
|
||||||
messagePropagationRecursive: (callback: any) => any;
|
messagePropagationRecursive: (callback: () => any) => any;
|
||||||
setAsync: (value: boolean) => void;
|
setAsync: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,9 +167,7 @@ export const getMessageBridgeFake = (): MessageBridgeFake => {
|
|||||||
await Promise.all(oldMessages.map((x) => wrapper(x.resolve)));
|
await Promise.all(oldMessages.map((x) => wrapper(x.resolve)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const messagePropagationRecursive = async (
|
const messagePropagationRecursive = async (wrapper = (callback: () => any) => callback()) => {
|
||||||
wrapper: (callback: any) => any = (callback) => callback(),
|
|
||||||
) => {
|
|
||||||
while (messagePropagationBuffer.size) {
|
while (messagePropagationBuffer.size) {
|
||||||
await messagePropagation(wrapper);
|
await messagePropagation(wrapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,7 @@ const render = (components: ReactApplicationHigherOrderComponent[]) => {
|
|||||||
export const ReactApplication = observer(({ di }: ReactApplicationProps) => {
|
export const ReactApplication = observer(({ di }: ReactApplicationProps) => {
|
||||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||||
|
|
||||||
const higherOrderComponents = computedInjectMany(
|
const higherOrderComponents = computedInjectMany(reactApplicationHigherOrderComponentInjectionToken);
|
||||||
reactApplicationHigherOrderComponentInjectionToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
const Components = [...higherOrderComponents.get(), ReactApplicationContent];
|
const Components = [...higherOrderComponents.get(), ReactApplicationContent];
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,4 @@ export type {
|
|||||||
QuerySingleElement,
|
QuerySingleElement,
|
||||||
} from "./src/discovery-of-html-elements";
|
} from "./src/discovery-of-html-elements";
|
||||||
|
|
||||||
export {
|
export { discoverFor, getSingleElement, queryAllElements, querySingleElement } from "./src/discovery-of-html-elements";
|
||||||
discoverFor,
|
|
||||||
getSingleElement,
|
|
||||||
queryAllElements,
|
|
||||||
querySingleElement,
|
|
||||||
} from "./src/discovery-of-html-elements";
|
|
||||||
|
|||||||
@ -26,8 +26,7 @@ export interface Discover {
|
|||||||
getSingleElement: GetSingleElement;
|
getSingleElement: GetSingleElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBaseElement = (source: DiscoverySourceTypes) =>
|
const getBaseElement = (source: DiscoverySourceTypes) => ("baseElement" in source ? source.baseElement : source);
|
||||||
"baseElement" in source ? source.baseElement : source;
|
|
||||||
|
|
||||||
export function querySingleElement(getSource: () => DiscoverySourceTypes): QuerySingleElement {
|
export function querySingleElement(getSource: () => DiscoverySourceTypes): QuerySingleElement {
|
||||||
return (attributeName, attributeValue) => {
|
return (attributeName, attributeValue) => {
|
||||||
@ -35,9 +34,7 @@ export function querySingleElement(getSource: () => DiscoverySourceTypes): Query
|
|||||||
|
|
||||||
const dataAttribute = `data-${attributeName}-test`;
|
const dataAttribute = `data-${attributeName}-test`;
|
||||||
|
|
||||||
const selector = attributeValue
|
const selector = attributeValue ? `[${dataAttribute}="${attributeValue}"]` : `[${dataAttribute}]`;
|
||||||
? `[${dataAttribute}="${attributeValue}"]`
|
|
||||||
: `[${dataAttribute}]`;
|
|
||||||
|
|
||||||
const discovered = getBaseElement(source).querySelector(selector);
|
const discovered = getBaseElement(source).querySelector(selector);
|
||||||
|
|
||||||
@ -78,10 +75,7 @@ export function getSingleElement(getSource: () => DiscoverySourceTypes): GetSing
|
|||||||
return (attributeName, attributeValue) => {
|
return (attributeName, attributeValue) => {
|
||||||
const dataAttribute = `data-${attributeName}-test`;
|
const dataAttribute = `data-${attributeName}-test`;
|
||||||
|
|
||||||
const { discovered, ...nestedDiscover } = querySingleElement(getSource)(
|
const { discovered, ...nestedDiscover } = querySingleElement(getSource)(attributeName, attributeValue);
|
||||||
attributeName,
|
|
||||||
attributeValue,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!discovered) {
|
if (!discovered) {
|
||||||
// eslint-disable-next-line xss/no-mixed-html
|
// eslint-disable-next-line xss/no-mixed-html
|
||||||
@ -97,18 +91,14 @@ export function getSingleElement(getSource: () => DiscoverySourceTypes): GetSing
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(`Couldn't find HTML-element with attribute "${dataAttribute}"\n\nHTML is:\n\n${html}`);
|
||||||
`Couldn't find HTML-element with attribute "${dataAttribute}"\n\nHTML is:\n\n${html}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const click = () => {
|
const click = () => {
|
||||||
if ("click" in discovered && typeof discovered.click === "function") {
|
if ("click" in discovered && typeof discovered.click === "function") {
|
||||||
discovered.click();
|
discovered.click();
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(`Tried to click something that was not clickable:\n\n${prettyDom(discovered)}`);
|
||||||
`Tried to click something that was not clickable:\n\n${prettyDom(discovered)}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user