Files
lens/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts
T
Janne SavolainenandMikko Aspiala 0f1f030a06 Switch to using messaging and startable stoppable from NPM package (#7368)
* Add custom jest resolver to fix requiring "uuid" module

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update dependencies

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce test utils for rendering and running with thrown mobx reactions

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract startable-stoppable to NPM package

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract messaging to NPM package

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using startable-stoppable from NPM package

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using messaging from the Feature

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove old implementation of messaging

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make setupping app paths happen earlier in renderer

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix typo

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add kludge to make testing-library work properly from test-utils package

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix code style

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add lint:fix -root script

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix unrelated failing unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Turn of no-floating-promises from typescript linting for being broken

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make linting not happen for dist -directories

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make linting failures appear as failure

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Stop running prettier twice

It already gets ran as eslint-plugin.

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make CI run unit tests for all packages by consolidating name of NPM script

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add missing unit tests for coverage

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Skip coverage for test utils

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove check for coverage in packages which are not ready for it

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Stop collecting coverage from index.ts files them being indirections to the implementation

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Implement sending message to channel in main

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add missing feature dependencies

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add dummy implementations for requesting in main from renderer

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Re-enable communicating from main to cluster frames

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Ignore trivial files from coverage

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update package-lock

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract message-bridge to separate NPM package to prevent dev dependencies being in the production bundle

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract computed channel to own NPM package for clear dependencies

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate electron related stuff to a directory

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add missing publish configurations

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Ignore test implementation from coverage being not interesting

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

---------

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
2023-03-21 11:38:43 +02:00

197 lines
5.6 KiB
TypeScript

import type { DiContainer } from "@ogre-tools/injectable";
import type { Channel, MessageChannelHandler, RequestChannelHandler } from "@k8slens/messaging";
import {
enlistMessageChannelListenerInjectionToken,
enlistRequestChannelListenerInjectionToken,
RequestFromChannel,
requestFromChannelInjectionToken,
sendMessageToChannelInjectionToken,
} from "@k8slens/messaging";
import { pipeline } from "@ogre-tools/fp";
import { filter, map } from "lodash/fp";
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
export type MessageBridgeFake = {
involve: (...dis: DiContainer[]) => void;
messagePropagation: () => Promise<void>;
messagePropagationRecursive: (callback: any) => any;
setAsync: (value: boolean) => void;
};
const overrideMessaging = ({
di,
messageListenersByDi,
messagePropagationBuffer,
getAsyncModeStatus,
}: {
di: DiContainer;
messageListenersByDi: Map<DiContainer, Map<string, Set<MessageChannelHandler<Channel>>>>;
messagePropagationBuffer: Set<{ resolve: () => Promise<void> }>;
getAsyncModeStatus: () => boolean;
}) => {
const messageHandlersByChannel = new Map<string, Set<MessageChannelHandler<Channel>>>();
messageListenersByDi.set(di, messageHandlersByChannel);
di.override(sendMessageToChannelInjectionToken, () => (channel, message) => {
const allOtherDis = [...messageListenersByDi.keys()].filter((x) => x !== di);
allOtherDis.forEach((otherDi) => {
const listeners = messageListenersByDi.get(otherDi);
const handlersForChannel = listeners?.get(channel.id);
if (!handlersForChannel) {
return;
}
if (getAsyncModeStatus()) {
const resolvableHandlePromise = asyncFn();
resolvableHandlePromise().then(() => {
handlersForChannel.forEach((handler) => handler(message, { frameId: 42, processId: 42 }));
});
messagePropagationBuffer.add(resolvableHandlePromise);
} else {
handlersForChannel.forEach((handler) => handler(message, { frameId: 42, processId: 42 }));
}
});
});
di.override(enlistMessageChannelListenerInjectionToken, () => (listener) => {
if (!messageHandlersByChannel.has(listener.channel.id)) {
messageHandlersByChannel.set(listener.channel.id, new Set());
}
const handlerSet = messageHandlersByChannel.get(listener.channel.id);
handlerSet?.add(listener.handler);
return () => {
handlerSet?.delete(listener.handler);
};
});
};
const overrideRequesting = ({
di,
requestListenersByDi,
}: {
di: DiContainer;
requestListenersByDi: Map<DiContainer, Map<string, Set<RequestChannelHandler<Channel>>>>;
}) => {
const requestHandlersByChannel = new Map<string, Set<RequestChannelHandler<Channel>>>();
requestListenersByDi.set(di, requestHandlersByChannel);
di.override(
requestFromChannelInjectionToken,
() =>
(async (channel, request) =>
pipeline(
[...requestListenersByDi.values()],
map((listenersByChannel) => listenersByChannel?.get(channel.id)),
filter((x) => !!x),
(channelSpecificListeners) => {
if (channelSpecificListeners.length === 0) {
throw new Error(
`Tried to make a request but no listeners for channel "${channel.id}" was discovered in any DIs`,
);
}
if (channelSpecificListeners.length > 1) {
throw new Error(
`Tried to make a request but multiple listeners were discovered for channel "${channel.id}" in multiple DIs.`,
);
}
const listeners = channelSpecificListeners[0];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const [handler] = listeners!;
return handler;
},
async (handler) => handler(request),
)) as RequestFromChannel,
);
di.override(enlistRequestChannelListenerInjectionToken, () => (listener) => {
if (!requestHandlersByChannel.has(listener.channel.id)) {
requestHandlersByChannel.set(listener.channel.id, new Set());
}
const handlerSet = requestHandlersByChannel.get(listener.channel.id);
handlerSet?.add(listener.handler);
return () => {
handlerSet?.delete(listener.handler);
};
});
};
export const getMessageBridgeFake = (): MessageBridgeFake => {
const messageListenersByDi = new Map<
DiContainer,
Map<string, Set<MessageChannelHandler<Channel>>>
>();
const requestListenersByDi = new Map<
DiContainer,
Map<string, Set<RequestChannelHandler<Channel>>>
>();
const messagePropagationBuffer = new Set<AsyncFnMock<() => void>>();
const messagePropagation = async (wrapper: (callback: any) => any = (callback) => callback()) => {
const oldMessages = [...messagePropagationBuffer.values()];
messagePropagationBuffer.clear();
await Promise.all(oldMessages.map((x) => wrapper(x.resolve)));
};
const messagePropagationRecursive = async (
wrapper: (callback: any) => any = (callback) => callback(),
) => {
while (messagePropagationBuffer.size) {
await messagePropagation(wrapper);
}
};
let asyncModeStatus = false;
const getAsyncModeStatus = () => asyncModeStatus;
return {
involve: (...dis: DiContainer[]) => {
dis.forEach((di) => {
overrideRequesting({ di, requestListenersByDi });
overrideMessaging({
di,
messageListenersByDi,
messagePropagationBuffer,
getAsyncModeStatus,
});
});
},
messagePropagation,
messagePropagationRecursive,
setAsync: (value) => {
asyncModeStatus = value;
},
};
};