ca95edf295
* feat: compact theme * fix: theme * fix: styling * fix: margin * feat: improve * fix: remove console.log * test: enable plugin test * refactor: multi app * test: lazy load sync plugin * test: lazy load test * fix: beforeGetApplication Event * feat: loadFromDatabase options in traverseSubApps * fix: test * fix: multi app manager test * chore: test * test: should upgrade sub apps when main app upgrade * feat: plugin require check * chore: yarn.lock * fix: sql typo * feat: share collections * fix: record name * test: belongs to many repository * fix: belongs to many with targetKey alias * fix: extend collection error * fix: transaction error * feat: collection graph * fix: update options in collection * chore: collections graph * chore: export uitls * feat: connected nodes method in collections graph * feat: exclude params in connected nodes * chore: sub app collection list params * fix: collections graph * feat: syncToApps migration * fix: translation --------- Co-authored-by: chenos <chenlinxh@gmail.com>
78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import * as graphlib from 'graphlib';
|
|
import { castArray } from 'lodash';
|
|
|
|
type BuildGraphOptions = {
|
|
direction?: 'forward' | 'reverse';
|
|
collections: any[];
|
|
};
|
|
|
|
export class CollectionsGraph {
|
|
static graphlib() {
|
|
return graphlib;
|
|
}
|
|
|
|
static connectedNodes(options: BuildGraphOptions & { nodes: Array<string>; excludes?: Array<string> }) {
|
|
const nodes = castArray(options.nodes);
|
|
const excludes = castArray(options.excludes || []);
|
|
|
|
const graph = CollectionsGraph.build(options);
|
|
const connectedNodes = new Set();
|
|
for (const node of nodes) {
|
|
const connected = graphlib.alg.preorder(graph, node);
|
|
for (const connectedNode of connected) {
|
|
if (excludes.includes(connectedNode)) continue;
|
|
connectedNodes.add(connectedNode);
|
|
}
|
|
}
|
|
|
|
return Array.from(connectedNodes);
|
|
}
|
|
|
|
static preOrder(options: BuildGraphOptions & { node: string }) {
|
|
return CollectionsGraph.graphlib().alg.preorder(CollectionsGraph.build(options), options.node);
|
|
}
|
|
|
|
static build(options: BuildGraphOptions) {
|
|
const collections = options.collections;
|
|
const direction = options?.direction || 'forward';
|
|
const isForward = direction === 'forward';
|
|
|
|
const graph = new graphlib.Graph();
|
|
|
|
for (const collection of collections) {
|
|
graph.setNode(collection.name);
|
|
}
|
|
|
|
for (const collection of collections) {
|
|
const parents = collection.inherits || [];
|
|
for (const parent of parents) {
|
|
if (isForward) {
|
|
graph.setEdge(collection.name, parent);
|
|
} else {
|
|
graph.setEdge(parent, collection.name);
|
|
}
|
|
}
|
|
|
|
for (const field of collection.fields || []) {
|
|
if (field.type === 'hasMany' || field.type === 'belongsTo' || field.type === 'hasOne') {
|
|
isForward ? graph.setEdge(collection.name, field.target) : graph.setEdge(field.target, collection.name);
|
|
}
|
|
|
|
if (field.type === 'belongsToMany') {
|
|
const throughCollection = field.through;
|
|
|
|
if (isForward) {
|
|
graph.setEdge(collection.name, throughCollection);
|
|
graph.setEdge(throughCollection, field.target);
|
|
} else {
|
|
graph.setEdge(field.target, throughCollection);
|
|
graph.setEdge(throughCollection, collection.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return graph;
|
|
}
|
|
}
|