mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add support for multiple "runAfter" runnables
- Needed so that several dependencies can be declared Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
f9084bc2b7
commit
5b80dfc70a
@ -3,46 +3,81 @@
|
|||||||
* 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 { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable";
|
import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { Composite } from "../utils/composite/get-composite/get-composite";
|
import type { SingleOrMany } from "../utils";
|
||||||
import { getCompositeFor } from "../utils/composite/get-composite/get-composite";
|
import { getOrInsertSet, isDefined } from "../utils";
|
||||||
|
import { observable, when } from "mobx";
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
export interface Runnable<TParameter = void> {
|
export interface Runnable<TParameter = void> {
|
||||||
id: string;
|
id: string;
|
||||||
run: Run<TParameter>;
|
run: Run<TParameter>;
|
||||||
runAfter?: Runnable<TParameter>;
|
runAfter?: SingleOrMany<Runnable<TParameter>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Run<Param> = (parameter: Param) => Promise<void> | void;
|
type Run<Param> = (parameter: Param) => Promise<void> | void;
|
||||||
|
|
||||||
export type RunMany = <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => Run<Param>;
|
export type RunMany = <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => Run<Param>;
|
||||||
|
|
||||||
async function runCompositeRunnables<Param>(param: Param, composite: Composite<Runnable<Param>>) {
|
const computedNextEdge = (traversed: string[], graph: Map<string, Set<string>>, currentId: string) => {
|
||||||
await composite.value.run(param);
|
const currentNode = graph.get(currentId);
|
||||||
await Promise.all(composite.children.map(composite => runCompositeRunnables(param, composite)));
|
|
||||||
}
|
assert(currentNode, `Runnable graph does not contain node with id="${currentId}"`);
|
||||||
|
|
||||||
|
for (const nextId of currentNode.values()) {
|
||||||
|
if (traversed.includes(nextId)) {
|
||||||
|
throw new Error(`Cycle in runnable graph: "${traversed.join(`" -> "`)}" -> "${nextId}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
computedNextEdge([...traversed, nextId], graph, nextId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyRunnablesAreDAG = <Param>(runnables: Runnable<Param>[]) => {
|
||||||
|
const rootId = uuid.v4();
|
||||||
|
const runnableGraph = new Map<string, Set<string>>();
|
||||||
|
|
||||||
|
// Build the Directed graph
|
||||||
|
for (const runnable of runnables) {
|
||||||
|
getOrInsertSet(runnableGraph, runnable.id);
|
||||||
|
|
||||||
|
if (!runnable.runAfter) {
|
||||||
|
getOrInsertSet(runnableGraph, rootId).add(runnable.id);
|
||||||
|
} else if (Array.isArray(runnable.runAfter)) {
|
||||||
|
for (const parentRunnable of runnable.runAfter) {
|
||||||
|
getOrInsertSet(runnableGraph, parentRunnable.id).add(runnable.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getOrInsertSet(runnableGraph, runnable.runAfter.id).add(runnable.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a DFS to find any cycles
|
||||||
|
computedNextEdge([], runnableGraph, rootId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const executeRunnableWith = <Param>(param: Param) => {
|
||||||
|
const finishedIds = observable.set<string>();
|
||||||
|
|
||||||
|
return async (runnable: Runnable<Param>): Promise<void> => {
|
||||||
|
const parentRunnables = [runnable.runAfter].flat().filter(isDefined);
|
||||||
|
|
||||||
|
for (const parentRunnable of parentRunnables) {
|
||||||
|
await when(() => finishedIds.has(parentRunnable.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
await runnable.run(param);
|
||||||
|
finishedIds.add(runnable.id);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function runManyFor(di: DiContainerForInjection): RunMany {
|
export function runManyFor(di: DiContainerForInjection): RunMany {
|
||||||
return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => {
|
return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => {
|
||||||
|
const executeRunnable = executeRunnableWith(param);
|
||||||
const allRunnables = di.injectMany(injectionToken);
|
const allRunnables = di.injectMany(injectionToken);
|
||||||
const rootId = uuid.v4();
|
|
||||||
const getCompositeRunnables = getCompositeFor<Runnable<Param>>({
|
|
||||||
getId: (runnable) => runnable.id,
|
|
||||||
getParentId: (runnable) => (
|
|
||||||
runnable.id === rootId
|
|
||||||
? undefined
|
|
||||||
: runnable.runAfter?.id ?? rootId
|
|
||||||
),
|
|
||||||
});
|
|
||||||
const composite = getCompositeRunnables([
|
|
||||||
// This is a dummy runnable to conform to the requirements of `getCompositeFor` to only have one root
|
|
||||||
{
|
|
||||||
id: rootId,
|
|
||||||
run: () => {},
|
|
||||||
},
|
|
||||||
...allRunnables,
|
|
||||||
]);
|
|
||||||
|
|
||||||
await runCompositeRunnables(param, composite);
|
verifyRunnablesAreDAG(allRunnables);
|
||||||
|
|
||||||
|
await Promise.all(allRunnables.map(executeRunnable));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user