@webda/shell
Version:
Deploy a Webda app or configure it
150 lines (148 loc) • 4.85 kB
JavaScript
/*
StorageDiagram
<!-- WEBDA:StorageDiagram -->
```mermaid
flowchart BT
CoreModel
User --> CoreModel
Task --> CoreModel
CustomUser --> User
subgraph MyStore
User
CustomUser
end
```
<!-- /WEBDA:StorageDiagram -->
*/
import { Service } from "@webda/core";
import { existsSync, readFileSync, writeFileSync } from "fs";
/**
* Abstract diagram with the common replacement methods
*/
export class Diagram {
constructor(name) {
this.name = name;
}
update(file, webda) {
this.file = file;
if (existsSync(file)) {
this.content = readFileSync(file, "utf8").toString();
}
else {
this.content = "";
}
let diagram = this.generate(webda);
diagram = `<!-- WEBDA:${this.name} -->\n${diagram}\n<!-- /WEBDA:${this.name} -->`;
if (this.content.includes(`<!-- WEBDA:${this.name} -->`)) {
const regexp = new RegExp(`<!-- WEBDA:${this.name} -->[\\s\\S]*?<!-- \\/WEBDA:${this.name} -->`, "gm");
this.content = this.content.replace(regexp, diagram);
}
else {
this.content += `\n${diagram}\n`;
}
writeFileSync(this.file, this.content);
}
}
/**
* Export each CoreModel and their Store
*/
export class StorageDiagram extends Diagram {
constructor() {
super("StorageDiagram");
}
generate(webda) {
let diagram = "```mermaid\nflowchart BT\n";
const tree = webda.getApplication().getModelHierarchy("CoreModel");
const stores = {};
const recursive = (name, children) => {
let storeName = webda.getModelStore(webda.getModel(name)).getName();
stores[storeName] ?? (stores[storeName] = []);
stores[storeName].push(name);
for (let child in children) {
diagram += `\t${child} --> ${name}\n`;
recursive(child, children[child]);
}
};
recursive("CoreModel", tree.children);
// Add subgraph for Stores
for (let store in stores) {
diagram += `\n\tsubgraph ${store}\n`;
for (let model of stores[store]) {
diagram += `\t\t${model}\n`;
}
diagram += `\tend\n`;
}
return diagram + "```";
}
}
/**
* Export each CoreModel, their properties and actions
*/
export class ModelDiagram extends Diagram {
constructor() {
super("ClassDiagram");
}
generateClassDefinition(schema, actions) {
let definition = "";
if (schema.description) {
definition += `\t\t${schema.title}: ${schema.description}\n`;
}
const properties = schema.properties;
// Display properties
for (const propertyName in properties) {
const property = properties[propertyName];
const isRequired = schema.required && schema.required.includes(propertyName);
const propertyType = property.type;
definition += `\t\t${isRequired ? "+" : "-"}${propertyName}: ${propertyType}\n`;
}
// Display actions
for (const actionName in actions) {
definition += `\t\t+${actionName}()\n`;
}
return definition;
}
generate(webda) {
const models = webda.getApplication().getModels();
let diagram = "```mermaid\nclassDiagram\n";
Object.values(models).forEach(model => {
diagram += `\tclass ${model.getIdentifier()}{\n`;
diagram += this.generateClassDefinition(model.getSchema() || { properties: {} }, model.getActions());
diagram += `\t}\n`;
});
return diagram + "```";
}
}
/**
* Export each Service and their dependencies
*
* It detect dependencies by looking at the attributes of the service
* So dynamic dependencies are not detected
*/
export class ServiceDiagram extends Diagram {
constructor() {
super("ServiceDiagram");
}
generate(webda) {
const services = Object.values(webda.getServices()).filter(service => service.getName);
let diagram = "```mermaid\nflowchart TD\n";
let ids = {};
services.forEach((service, i) => {
diagram += `\tS${i}(${service.getName()}<br /><i>${service.getParameters().type}</i>)\n`;
ids[service.getName()] = i;
});
services.forEach((service, i) => {
for (let attr in service) {
if (service[attr] instanceof Service) {
diagram += `\tS${i} -->|${attr}| S${ids[service[attr].getName()]}\n`;
}
}
});
return diagram + "```";
}
}
export const DiagramTypes = {
storage: StorageDiagram,
models: ModelDiagram,
services: ServiceDiagram
};
//# sourceMappingURL=diagrams.js.map