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.
|
||||
*/
|
||||
import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable";
|
||||
import type { Composite } from "../utils/composite/get-composite/get-composite";
|
||||
import { getCompositeFor } from "../utils/composite/get-composite/get-composite";
|
||||
import type { SingleOrMany } from "../utils";
|
||||
import { getOrInsertSet, isDefined } from "../utils";
|
||||
import { observable, when } from "mobx";
|
||||
import * as uuid from "uuid";
|
||||
import assert from "assert";
|
||||
|
||||
export interface Runnable<TParameter = void> {
|
||||
id: string;
|
||||
run: Run<TParameter>;
|
||||
runAfter?: Runnable<TParameter>;
|
||||
runAfter?: SingleOrMany<Runnable<TParameter>>;
|
||||
}
|
||||
|
||||
type Run<Param> = (parameter: Param) => Promise<void> | void;
|
||||
|
||||
export type RunMany = <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => Run<Param>;
|
||||
|
||||
async function runCompositeRunnables<Param>(param: Param, composite: Composite<Runnable<Param>>) {
|
||||
await composite.value.run(param);
|
||||
await Promise.all(composite.children.map(composite => runCompositeRunnables(param, composite)));
|
||||
}
|
||||
const computedNextEdge = (traversed: string[], graph: Map<string, Set<string>>, currentId: string) => {
|
||||
const currentNode = graph.get(currentId);
|
||||
|
||||
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 {
|
||||
return <Param>(injectionToken: InjectionToken<Runnable<Param>, void>) => async (param: Param) => {
|
||||
const executeRunnable = executeRunnableWith(param);
|
||||
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