@cyberismo/data-handler 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +702 -0
- package/dist/card-metadata-updater.d.ts +33 -0
- package/dist/card-metadata-updater.js +121 -0
- package/dist/card-metadata-updater.js.map +1 -0
- package/dist/command-handler.d.ts +96 -0
- package/dist/command-handler.js +557 -0
- package/dist/command-handler.js.map +1 -0
- package/dist/command-manager.d.ts +43 -0
- package/dist/command-manager.js +73 -0
- package/dist/command-manager.js.map +1 -0
- package/dist/commands/calculate.d.ts +86 -0
- package/dist/commands/calculate.js +444 -0
- package/dist/commands/calculate.js.map +1 -0
- package/dist/commands/create.d.ts +114 -0
- package/dist/commands/create.js +389 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/edit.d.ts +37 -0
- package/dist/commands/edit.js +99 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/export-site.d.ts +45 -0
- package/dist/commands/export-site.js +301 -0
- package/dist/commands/export-site.js.map +1 -0
- package/dist/commands/export.d.ts +53 -0
- package/dist/commands/export.js +251 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/import.d.ts +53 -0
- package/dist/commands/import.js +133 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/index.d.ts +26 -0
- package/dist/commands/index.js +27 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/move.d.ts +55 -0
- package/dist/commands/move.js +341 -0
- package/dist/commands/move.js.map +1 -0
- package/dist/commands/remove.d.ts +38 -0
- package/dist/commands/remove.js +192 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/rename.d.ts +46 -0
- package/dist/commands/rename.js +289 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/show.d.ts +124 -0
- package/dist/commands/show.js +345 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/transition.d.ts +27 -0
- package/dist/commands/transition.js +92 -0
- package/dist/commands/transition.js.map +1 -0
- package/dist/commands/update.d.ts +29 -0
- package/dist/commands/update.js +64 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +143 -0
- package/dist/commands/validate.js +689 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/containers/card-container.d.ts +44 -0
- package/dist/containers/card-container.js +282 -0
- package/dist/containers/card-container.js.map +1 -0
- package/dist/containers/project/project-paths.d.ts +46 -0
- package/dist/containers/project/project-paths.js +105 -0
- package/dist/containers/project/project-paths.js.map +1 -0
- package/dist/containers/project/resource-collector.d.ts +86 -0
- package/dist/containers/project/resource-collector.js +331 -0
- package/dist/containers/project/resource-collector.js.map +1 -0
- package/dist/containers/project.d.ts +351 -0
- package/dist/containers/project.js +896 -0
- package/dist/containers/project.js.map +1 -0
- package/dist/containers/template.d.ts +108 -0
- package/dist/containers/template.js +433 -0
- package/dist/containers/template.js.map +1 -0
- package/dist/exceptions/index.d.ts +19 -0
- package/dist/exceptions/index.js +26 -0
- package/dist/exceptions/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/adoc.d.ts +12 -0
- package/dist/interfaces/adoc.js +13 -0
- package/dist/interfaces/adoc.js.map +1 -0
- package/dist/interfaces/macros.d.ts +45 -0
- package/dist/interfaces/macros.js +13 -0
- package/dist/interfaces/macros.js.map +1 -0
- package/dist/interfaces/project-interfaces.d.ts +121 -0
- package/dist/interfaces/project-interfaces.js +21 -0
- package/dist/interfaces/project-interfaces.js.map +1 -0
- package/dist/interfaces/request-status-interfaces.d.ts +28 -0
- package/dist/interfaces/request-status-interfaces.js +20 -0
- package/dist/interfaces/request-status-interfaces.js.map +1 -0
- package/dist/interfaces/resource-interfaces.d.ts +117 -0
- package/dist/interfaces/resource-interfaces.js +20 -0
- package/dist/interfaces/resource-interfaces.js.map +1 -0
- package/dist/macros/base-macro.d.ts +31 -0
- package/dist/macros/base-macro.js +126 -0
- package/dist/macros/base-macro.js.map +1 -0
- package/dist/macros/common.d.ts +17 -0
- package/dist/macros/common.js +23 -0
- package/dist/macros/common.js.map +1 -0
- package/dist/macros/createCards/index.d.ts +36 -0
- package/dist/macros/createCards/index.js +35 -0
- package/dist/macros/createCards/index.js.map +1 -0
- package/dist/macros/createCards/metadata.d.ts +14 -0
- package/dist/macros/createCards/metadata.js +18 -0
- package/dist/macros/createCards/metadata.js.map +1 -0
- package/dist/macros/graph/index.d.ts +29 -0
- package/dist/macros/graph/index.js +91 -0
- package/dist/macros/graph/index.js.map +1 -0
- package/dist/macros/graph/metadata.d.ts +14 -0
- package/dist/macros/graph/metadata.js +18 -0
- package/dist/macros/graph/metadata.js.map +1 -0
- package/dist/macros/index.d.ts +93 -0
- package/dist/macros/index.js +237 -0
- package/dist/macros/index.js.map +1 -0
- package/dist/macros/report/index.d.ts +26 -0
- package/dist/macros/report/index.js +70 -0
- package/dist/macros/report/index.js.map +1 -0
- package/dist/macros/report/metadata.d.ts +14 -0
- package/dist/macros/report/metadata.js +18 -0
- package/dist/macros/report/metadata.js.map +1 -0
- package/dist/macros/scoreCard/index.d.ts +30 -0
- package/dist/macros/scoreCard/index.js +38 -0
- package/dist/macros/scoreCard/index.js.map +1 -0
- package/dist/macros/scoreCard/metadata.d.ts +14 -0
- package/dist/macros/scoreCard/metadata.js +18 -0
- package/dist/macros/scoreCard/metadata.js.map +1 -0
- package/dist/macros/task-queue.d.ts +46 -0
- package/dist/macros/task-queue.js +69 -0
- package/dist/macros/task-queue.js.map +1 -0
- package/dist/module-manager.d.ts +62 -0
- package/dist/module-manager.js +350 -0
- package/dist/module-manager.js.map +1 -0
- package/dist/permissions/action-guard.d.ts +28 -0
- package/dist/permissions/action-guard.js +61 -0
- package/dist/permissions/action-guard.js.map +1 -0
- package/dist/project-settings.d.ts +42 -0
- package/dist/project-settings.js +120 -0
- package/dist/project-settings.js.map +1 -0
- package/dist/resources/array-handler.d.ts +28 -0
- package/dist/resources/array-handler.js +116 -0
- package/dist/resources/array-handler.js.map +1 -0
- package/dist/resources/card-type-resource.d.ts +72 -0
- package/dist/resources/card-type-resource.js +334 -0
- package/dist/resources/card-type-resource.js.map +1 -0
- package/dist/resources/create-defaults.d.ts +81 -0
- package/dist/resources/create-defaults.js +184 -0
- package/dist/resources/create-defaults.js.map +1 -0
- package/dist/resources/field-type-resource.d.ts +88 -0
- package/dist/resources/field-type-resource.js +411 -0
- package/dist/resources/field-type-resource.js.map +1 -0
- package/dist/resources/file-resource.d.ts +50 -0
- package/dist/resources/file-resource.js +301 -0
- package/dist/resources/file-resource.js.map +1 -0
- package/dist/resources/folder-resource.d.ts +66 -0
- package/dist/resources/folder-resource.js +100 -0
- package/dist/resources/folder-resource.js.map +1 -0
- package/dist/resources/graph-model-resource.d.ts +78 -0
- package/dist/resources/graph-model-resource.js +164 -0
- package/dist/resources/graph-model-resource.js.map +1 -0
- package/dist/resources/graph-view-resource.d.ts +78 -0
- package/dist/resources/graph-view-resource.js +163 -0
- package/dist/resources/graph-view-resource.js.map +1 -0
- package/dist/resources/link-type-resource.d.ts +62 -0
- package/dist/resources/link-type-resource.js +150 -0
- package/dist/resources/link-type-resource.js.map +1 -0
- package/dist/resources/report-resource.d.ts +77 -0
- package/dist/resources/report-resource.js +171 -0
- package/dist/resources/report-resource.js.map +1 -0
- package/dist/resources/resource-object.d.ts +108 -0
- package/dist/resources/resource-object.js +147 -0
- package/dist/resources/resource-object.js.map +1 -0
- package/dist/resources/template-resource.d.ts +82 -0
- package/dist/resources/template-resource.js +173 -0
- package/dist/resources/template-resource.js.map +1 -0
- package/dist/resources/workflow-resource.d.ts +67 -0
- package/dist/resources/workflow-resource.js +156 -0
- package/dist/resources/workflow-resource.js.map +1 -0
- package/dist/types/queries.d.ts +142 -0
- package/dist/types/queries.js +16 -0
- package/dist/types/queries.js.map +1 -0
- package/dist/utils/card-utils.d.ts +34 -0
- package/dist/utils/card-utils.js +78 -0
- package/dist/utils/card-utils.js.map +1 -0
- package/dist/utils/clingo-fact-builder.d.ts +58 -0
- package/dist/utils/clingo-fact-builder.js +126 -0
- package/dist/utils/clingo-fact-builder.js.map +1 -0
- package/dist/utils/clingo-facts.d.ts +97 -0
- package/dist/utils/clingo-facts.js +352 -0
- package/dist/utils/clingo-facts.js.map +1 -0
- package/dist/utils/clingo-parser.d.ts +59 -0
- package/dist/utils/clingo-parser.js +403 -0
- package/dist/utils/clingo-parser.js.map +1 -0
- package/dist/utils/clingo-program-builder.d.ts +39 -0
- package/dist/utils/clingo-program-builder.js +57 -0
- package/dist/utils/clingo-program-builder.js.map +1 -0
- package/dist/utils/common-utils.d.ts +24 -0
- package/dist/utils/common-utils.js +47 -0
- package/dist/utils/common-utils.js.map +1 -0
- package/dist/utils/constants.d.ts +18 -0
- package/dist/utils/constants.js +27 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/csv.d.ts +18 -0
- package/dist/utils/csv.js +45 -0
- package/dist/utils/csv.js.map +1 -0
- package/dist/utils/file-utils.d.ts +69 -0
- package/dist/utils/file-utils.js +158 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/json.d.ts +61 -0
- package/dist/utils/json.js +108 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/lexorank.d.ts +59 -0
- package/dist/utils/lexorank.js +159 -0
- package/dist/utils/lexorank.js.map +1 -0
- package/dist/utils/log-utils.d.ts +40 -0
- package/dist/utils/log-utils.js +109 -0
- package/dist/utils/log-utils.js.map +1 -0
- package/dist/utils/random.d.ts +19 -0
- package/dist/utils/random.js +34 -0
- package/dist/utils/random.js.map +1 -0
- package/dist/utils/resource-utils.d.ts +45 -0
- package/dist/utils/resource-utils.js +137 -0
- package/dist/utils/resource-utils.js.map +1 -0
- package/dist/utils/sanitize-svg.d.ts +18 -0
- package/dist/utils/sanitize-svg.js +38 -0
- package/dist/utils/sanitize-svg.js.map +1 -0
- package/dist/utils/user-preferences.d.ts +64 -0
- package/dist/utils/user-preferences.js +106 -0
- package/dist/utils/user-preferences.js.map +1 -0
- package/dist/utils/validate.d.ts +26 -0
- package/dist/utils/validate.js +53 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/utils/value-utils.d.ts +58 -0
- package/dist/utils/value-utils.js +181 -0
- package/dist/utils/value-utils.js.map +1 -0
- package/package.json +67 -0
- package/src/card-metadata-updater.ts +182 -0
- package/src/command-handler.ts +686 -0
- package/src/command-manager.ts +99 -0
- package/src/commands/calculate.ts +591 -0
- package/src/commands/create.ts +559 -0
- package/src/commands/edit.ts +123 -0
- package/src/commands/export-site.ts +356 -0
- package/src/commands/export.ts +315 -0
- package/src/commands/import.ts +169 -0
- package/src/commands/index.ts +42 -0
- package/src/commands/move.ts +451 -0
- package/src/commands/remove.ts +244 -0
- package/src/commands/rename.ts +378 -0
- package/src/commands/show.ts +442 -0
- package/src/commands/transition.ts +127 -0
- package/src/commands/update.ts +76 -0
- package/src/commands/validate.ts +962 -0
- package/src/containers/card-container.ts +378 -0
- package/src/containers/project/project-paths.ts +127 -0
- package/src/containers/project/resource-collector.ts +379 -0
- package/src/containers/project.ts +1135 -0
- package/src/containers/template.ts +573 -0
- package/src/exceptions/index.ts +29 -0
- package/src/index.ts +33 -0
- package/src/interfaces/adoc.ts +18 -0
- package/src/interfaces/macros.ts +54 -0
- package/src/interfaces/project-interfaces.ts +208 -0
- package/src/interfaces/request-status-interfaces.ts +30 -0
- package/src/interfaces/resource-interfaces.ts +179 -0
- package/src/macros/base-macro.ts +176 -0
- package/src/macros/common.ts +24 -0
- package/src/macros/createCards/index.ts +57 -0
- package/src/macros/createCards/metadata.ts +21 -0
- package/src/macros/graph/index.ts +130 -0
- package/src/macros/graph/metadata.ts +21 -0
- package/src/macros/index.ts +321 -0
- package/src/macros/report/index.ts +88 -0
- package/src/macros/report/metadata.ts +21 -0
- package/src/macros/scoreCard/index.ts +55 -0
- package/src/macros/scoreCard/metadata.ts +21 -0
- package/src/macros/task-queue.ts +79 -0
- package/src/module-manager.ts +443 -0
- package/src/permissions/action-guard.ts +77 -0
- package/src/project-settings.ts +140 -0
- package/src/resources/array-handler.ts +141 -0
- package/src/resources/card-type-resource.ts +455 -0
- package/src/resources/create-defaults.ts +216 -0
- package/src/resources/field-type-resource.ts +533 -0
- package/src/resources/file-resource.ts +433 -0
- package/src/resources/folder-resource.ts +140 -0
- package/src/resources/graph-model-resource.ts +205 -0
- package/src/resources/graph-view-resource.ts +199 -0
- package/src/resources/link-type-resource.ts +191 -0
- package/src/resources/report-resource.ts +224 -0
- package/src/resources/resource-object.ts +246 -0
- package/src/resources/template-resource.ts +210 -0
- package/src/resources/workflow-resource.ts +205 -0
- package/src/types/queries.ts +149 -0
- package/src/utils/card-utils.ts +83 -0
- package/src/utils/clingo-fact-builder.ts +167 -0
- package/src/utils/clingo-facts.ts +550 -0
- package/src/utils/clingo-parser.ts +519 -0
- package/src/utils/clingo-program-builder.ts +71 -0
- package/src/utils/common-utils.ts +54 -0
- package/src/utils/constants.ts +32 -0
- package/src/utils/csv.ts +53 -0
- package/src/utils/file-utils.ts +182 -0
- package/src/utils/json.ts +118 -0
- package/src/utils/lexorank.ts +180 -0
- package/src/utils/log-utils.ts +127 -0
- package/src/utils/random.ts +37 -0
- package/src/utils/resource-utils.ts +180 -0
- package/src/utils/sanitize-svg.ts +46 -0
- package/src/utils/user-preferences.ts +126 -0
- package/src/utils/validate.ts +66 -0
- package/src/utils/value-utils.ts +189 -0
|
@@ -0,0 +1,1135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2024
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// node
|
|
16
|
+
import { type Dirent } from 'node:fs';
|
|
17
|
+
import { dirname, join, resolve, sep } from 'node:path';
|
|
18
|
+
import { readdir } from 'node:fs/promises';
|
|
19
|
+
|
|
20
|
+
import { CardContainer } from './card-container.js'; // base class
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
type Card,
|
|
24
|
+
type CardAttachment,
|
|
25
|
+
CardLocation,
|
|
26
|
+
type CardListContainer,
|
|
27
|
+
type CardMetadata,
|
|
28
|
+
CardNameRegEx,
|
|
29
|
+
type FetchCardDetails,
|
|
30
|
+
type MetadataContent,
|
|
31
|
+
type ModuleContent,
|
|
32
|
+
type ModuleSetting,
|
|
33
|
+
type ProjectFetchCardDetails,
|
|
34
|
+
type ProjectMetadata,
|
|
35
|
+
type ProjectSettings,
|
|
36
|
+
type Resource,
|
|
37
|
+
type ResourceFolderType,
|
|
38
|
+
} from '../interfaces/project-interfaces.js';
|
|
39
|
+
import { findParentPath } from '../utils/card-utils.js';
|
|
40
|
+
import { getFilesSync, pathExists } from '../utils/file-utils.js';
|
|
41
|
+
import { generateRandomString } from '../utils/random.js';
|
|
42
|
+
import { isTemplateCard } from '../utils/card-utils.js';
|
|
43
|
+
import { ProjectConfiguration } from '../project-settings.js';
|
|
44
|
+
import { ProjectPaths } from './project/project-paths.js';
|
|
45
|
+
import { readJsonFile } from '../utils/json.js';
|
|
46
|
+
import {
|
|
47
|
+
resourceName,
|
|
48
|
+
type ResourceName,
|
|
49
|
+
resourceNameToString,
|
|
50
|
+
} from '../utils/resource-utils.js';
|
|
51
|
+
import {
|
|
52
|
+
ResourcesFrom,
|
|
53
|
+
ResourceCollector,
|
|
54
|
+
} from './project/resource-collector.js';
|
|
55
|
+
import type { Template } from './template.js';
|
|
56
|
+
import { Validate } from '../commands/validate.js';
|
|
57
|
+
|
|
58
|
+
import { CardTypeResource } from '../resources/card-type-resource.js';
|
|
59
|
+
import { FieldTypeResource } from '../resources/field-type-resource.js';
|
|
60
|
+
import { GraphModelResource } from '../resources/graph-model-resource.js';
|
|
61
|
+
import { GraphViewResource } from '../resources/graph-view-resource.js';
|
|
62
|
+
import { LinkTypeResource } from '../resources/link-type-resource.js';
|
|
63
|
+
import { ReportResource } from '../resources/report-resource.js';
|
|
64
|
+
import { TemplateResource } from '../resources/template-resource.js';
|
|
65
|
+
import { WorkflowResource } from '../resources/workflow-resource.js';
|
|
66
|
+
|
|
67
|
+
// Re-export this, so that classes that use Project do not need to have separate import.
|
|
68
|
+
export { ResourcesFrom };
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Represents project folder.
|
|
72
|
+
*/
|
|
73
|
+
export class Project extends CardContainer {
|
|
74
|
+
private resources: ResourceCollector;
|
|
75
|
+
private projectPaths: ProjectPaths;
|
|
76
|
+
private settings: ProjectConfiguration;
|
|
77
|
+
private validator: Validate;
|
|
78
|
+
|
|
79
|
+
// Created resources are held in a cache.
|
|
80
|
+
// In the cache, key is resource name, and data is resource metadata (as JSON).
|
|
81
|
+
private createdResources = new Map<string, JSON>();
|
|
82
|
+
|
|
83
|
+
constructor(path: string) {
|
|
84
|
+
super(path, '');
|
|
85
|
+
|
|
86
|
+
this.settings = new ProjectConfiguration(
|
|
87
|
+
join(path, '.cards', 'local', Project.projectConfigFileName),
|
|
88
|
+
);
|
|
89
|
+
this.projectPaths = new ProjectPaths(path);
|
|
90
|
+
this.resources = new ResourceCollector(this);
|
|
91
|
+
|
|
92
|
+
this.containerName = this.settings.name;
|
|
93
|
+
// todo: implement project validation
|
|
94
|
+
this.validator = Validate.getInstance();
|
|
95
|
+
this.resources.collectLocalResources();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Finds specific module.
|
|
99
|
+
private async findModule(moduleName: string): Promise<Resource | undefined> {
|
|
100
|
+
return (await this.resources.resources('modules')).find(
|
|
101
|
+
(item) => item.name === moduleName && item.path,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Returns (local or all) resources of a given type.
|
|
106
|
+
// @todo: if this would be public, we could remove cardTypes(), fieldTypes(), ... and similar APIs
|
|
107
|
+
private async resourcesOfType(
|
|
108
|
+
type: ResourceFolderType,
|
|
109
|
+
from: ResourcesFrom = ResourcesFrom.localOnly,
|
|
110
|
+
): Promise<Resource[]> {
|
|
111
|
+
return this.resources.resources(type, from);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Add a given 'resource' to the local resource arrays.
|
|
116
|
+
* @param resource Resource to add.
|
|
117
|
+
*/
|
|
118
|
+
public addResource(resource: Resource, data: JSON) {
|
|
119
|
+
this.resources.add(resource);
|
|
120
|
+
this.createdResources.set(resource.name, data);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns an array of all the attachments in the project card's (excluding ones in templates).
|
|
125
|
+
* @returns all attachments in the project.
|
|
126
|
+
*/
|
|
127
|
+
public async attachments(): Promise<CardAttachment[]> {
|
|
128
|
+
return super.attachments(this.paths.cardRootFolder);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Returns an array of all the calculation files (*.lp) in the project.
|
|
133
|
+
* @param from Defines where resources are collected from.
|
|
134
|
+
* @returns array of all calculation files in the project.
|
|
135
|
+
*/
|
|
136
|
+
public async calculations(
|
|
137
|
+
from: ResourcesFrom = ResourcesFrom.localOnly,
|
|
138
|
+
): Promise<Resource[]> {
|
|
139
|
+
return this.resources.resources('calculations', from);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Returns path to card's attachment folder.
|
|
144
|
+
* @param cardKey card key
|
|
145
|
+
* @returns path to card's attachment folder.
|
|
146
|
+
*/
|
|
147
|
+
public async cardAttachmentFolder(cardKey: string): Promise<string> {
|
|
148
|
+
// Check if it is a template card.
|
|
149
|
+
if (await this.isTemplateCard(cardKey)) {
|
|
150
|
+
const cardPath = await this.cardFolder(cardKey);
|
|
151
|
+
return join(cardPath, 'a');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const pathToProjectCard = this.pathToCard(cardKey);
|
|
155
|
+
return pathToProjectCard
|
|
156
|
+
? join(this.paths.cardRootFolder, pathToProjectCard, 'a')
|
|
157
|
+
: '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns details (as defined by cardDetails) of a card.
|
|
162
|
+
* @param cardKey card key (project prefix and a number, e.g. test_1)
|
|
163
|
+
* @param cardDetails which card details are returned.
|
|
164
|
+
* @returns Card details, or undefined if the card cannot be found.
|
|
165
|
+
*/
|
|
166
|
+
public async cardDetailsById(
|
|
167
|
+
cardKey: string,
|
|
168
|
+
cardDetails: ProjectFetchCardDetails,
|
|
169
|
+
): Promise<Card | undefined> {
|
|
170
|
+
return this.findSpecificCard(cardKey, cardDetails);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns path to card's folder.
|
|
175
|
+
* @param cardKey card key
|
|
176
|
+
* @returns path to card's folder.
|
|
177
|
+
*/
|
|
178
|
+
public async cardFolder(cardKey: string): Promise<string> {
|
|
179
|
+
const found = await super.findCard(this.paths.cardRootFolder, cardKey);
|
|
180
|
+
if (found) {
|
|
181
|
+
return found.path;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const templates = await this.templates();
|
|
185
|
+
const templatePromises = templates.map(async (template) => {
|
|
186
|
+
const templateObject = new TemplateResource(
|
|
187
|
+
this,
|
|
188
|
+
resourceName(template.name),
|
|
189
|
+
).templateObject();
|
|
190
|
+
const templateCard = templateObject
|
|
191
|
+
? await templateObject.findSpecificCard(cardKey)
|
|
192
|
+
: undefined;
|
|
193
|
+
return templateCard ? templateCard.path : '';
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const templatePaths = await Promise.all(templatePromises);
|
|
197
|
+
return templatePaths.find((path) => path !== '') || '';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Splits card path to parts. Returns the parts.
|
|
202
|
+
* Returned parts are: prefix, card key, array of parents and template name. Template name is returned only for template cards.
|
|
203
|
+
* @param cardPath path to a card
|
|
204
|
+
* @returns card path logical parts
|
|
205
|
+
* todo: if prefix would be parameter; this could be static, or util method
|
|
206
|
+
*/
|
|
207
|
+
public cardPathParts(cardPath: string) {
|
|
208
|
+
const pathParts = cardPath.split(sep);
|
|
209
|
+
const cardKey = pathParts.at(pathParts.length - 1);
|
|
210
|
+
const parents = [];
|
|
211
|
+
let prefix = this.projectPrefix;
|
|
212
|
+
let template = '';
|
|
213
|
+
let startIndex = -1;
|
|
214
|
+
let templatesNameIndex = -1;
|
|
215
|
+
|
|
216
|
+
const cardRootIndex = pathParts.indexOf('cardRoot');
|
|
217
|
+
const projectInternalsIndex = pathParts.indexOf('.cards');
|
|
218
|
+
|
|
219
|
+
if (projectInternalsIndex === -1 && cardRootIndex >= 0) {
|
|
220
|
+
startIndex = projectInternalsIndex;
|
|
221
|
+
} else if (projectInternalsIndex >= 0 && cardRootIndex === -1) {
|
|
222
|
+
const templatesIndex = pathParts.indexOf('templates');
|
|
223
|
+
startIndex = templatesIndex;
|
|
224
|
+
if (templatesIndex === -1) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Invalid card path. Template card must have 'templates' in path`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const modulesIndex = pathParts.indexOf('modules');
|
|
230
|
+
if (modulesIndex !== -1) {
|
|
231
|
+
prefix = pathParts.at(modulesIndex + 1) || '';
|
|
232
|
+
}
|
|
233
|
+
templatesNameIndex = templatesIndex + 1;
|
|
234
|
+
template = `${prefix}/templates/${pathParts.at(templatesNameIndex)}`;
|
|
235
|
+
} else {
|
|
236
|
+
throw new Error(`Card must be either project card, or template card`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Look for parents in the path.
|
|
240
|
+
let previousWasParent = false;
|
|
241
|
+
for (let index = startIndex; index <= pathParts.length; index++) {
|
|
242
|
+
if (previousWasParent) {
|
|
243
|
+
previousWasParent = false;
|
|
244
|
+
parents.push(pathParts.at(index - 2));
|
|
245
|
+
}
|
|
246
|
+
const cardsSubFolder = pathParts.at(index) === 'c';
|
|
247
|
+
const ignoreOrNotTemplatesParent =
|
|
248
|
+
index - 1 !== templatesNameIndex || templatesNameIndex === -1;
|
|
249
|
+
if (cardsSubFolder && ignoreOrNotTemplatesParent) {
|
|
250
|
+
previousWasParent = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
cardKey: cardKey,
|
|
256
|
+
parents: parents,
|
|
257
|
+
prefix: prefix,
|
|
258
|
+
template: template,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Returns an array of all the cards in the project. Cards have content and metadata
|
|
264
|
+
* @param path Optional path from which to fetch the cards. Generally it is best to fetch from Project root, e.g. Project.cardRootFolder
|
|
265
|
+
* @param details Which details to include in the cards; by default only "content" and "metadata" are included.
|
|
266
|
+
* @returns all cards from the given path in the project.
|
|
267
|
+
*/
|
|
268
|
+
public async cards(
|
|
269
|
+
path: string = this.paths.cardRootFolder,
|
|
270
|
+
details: FetchCardDetails = { content: true, metadata: true },
|
|
271
|
+
): Promise<Card[]> {
|
|
272
|
+
return super.cards(path, details);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Returns an array of all the card types in the project.
|
|
277
|
+
* @param from Defines where resources are collected from.
|
|
278
|
+
* @returns array of all card types in the project.
|
|
279
|
+
*/
|
|
280
|
+
public async cardTypes(
|
|
281
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
282
|
+
): Promise<Resource[]> {
|
|
283
|
+
return this.resources.resources('cardTypes', from);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Updates all local resources.
|
|
288
|
+
*/
|
|
289
|
+
public collectLocalResources() {
|
|
290
|
+
this.resources.changed();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Updates all imported module resources.
|
|
295
|
+
*/
|
|
296
|
+
public async collectModuleResources() {
|
|
297
|
+
await this.resources.moduleImported();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Returns project configuration.
|
|
302
|
+
* @returns project configuration.
|
|
303
|
+
*/
|
|
304
|
+
public get configuration(): ProjectConfiguration {
|
|
305
|
+
return this.settings;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Creates a Template object from template Card. It is ensured that the template is part of project.
|
|
310
|
+
* @param card Card that is part of some template.
|
|
311
|
+
* @returns Template object, or undefined if card is not part of template.
|
|
312
|
+
*/
|
|
313
|
+
public createTemplateObjectFromCard(card: Card): Template | undefined {
|
|
314
|
+
if (!card || !card.path || !isTemplateCard(card)) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
const { template } = this.cardPathParts(card.path);
|
|
318
|
+
return new TemplateResource(this, resourceName(template)).templateObject();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Returns an array of all the field types in the project.
|
|
323
|
+
* @param from Defines where resources are collected from.
|
|
324
|
+
* @returns array of all field types in the project.
|
|
325
|
+
*/
|
|
326
|
+
public async fieldTypes(
|
|
327
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
328
|
+
): Promise<Resource[]> {
|
|
329
|
+
return this.resources.resources('fieldTypes', from);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Finds root of a project
|
|
334
|
+
* @param path Path where to start looking for the project root.
|
|
335
|
+
* @returns path to a project root, or empty string.
|
|
336
|
+
*/
|
|
337
|
+
public static async findProjectRoot(path: string): Promise<string> {
|
|
338
|
+
const currentPath = resolve(join(path, '.cards'));
|
|
339
|
+
if (pathExists(currentPath)) {
|
|
340
|
+
return path;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const parentPath = resolve(path, '..');
|
|
344
|
+
if (parentPath === path) {
|
|
345
|
+
return '';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return Project.findProjectRoot(parentPath);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Returns specific card.
|
|
353
|
+
* @param cardToFind Card key to find
|
|
354
|
+
* @param details Defines which card details are included in the return values.
|
|
355
|
+
* @returns specific card details, or undefined if card is not part of the project.
|
|
356
|
+
*/
|
|
357
|
+
public async findSpecificCard(
|
|
358
|
+
cardToFind: string,
|
|
359
|
+
details: ProjectFetchCardDetails = {},
|
|
360
|
+
): Promise<Card | undefined> {
|
|
361
|
+
let card;
|
|
362
|
+
|
|
363
|
+
if (
|
|
364
|
+
details.location === CardLocation.projectOnly ||
|
|
365
|
+
details.location === CardLocation.all ||
|
|
366
|
+
!details.location
|
|
367
|
+
) {
|
|
368
|
+
card = await super.findCard(
|
|
369
|
+
this.paths.cardRootFolder,
|
|
370
|
+
cardToFind,
|
|
371
|
+
details,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
!card &&
|
|
377
|
+
(details.location === CardLocation.templatesOnly ||
|
|
378
|
+
details.location === CardLocation.all ||
|
|
379
|
+
!details.location)
|
|
380
|
+
) {
|
|
381
|
+
const templates = await this.templates();
|
|
382
|
+
for (const template of templates) {
|
|
383
|
+
const templateObject = new TemplateResource(
|
|
384
|
+
this,
|
|
385
|
+
resourceName(template.name),
|
|
386
|
+
).templateObject();
|
|
387
|
+
if (!templateObject) continue;
|
|
388
|
+
|
|
389
|
+
// optimize: execute each find in template parallel
|
|
390
|
+
card = await templateObject.findSpecificCard(cardToFind, details);
|
|
391
|
+
if (card) {
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return card;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Returns an array of all the graph models in the project.
|
|
401
|
+
* @param from Defines where resources are collected from.
|
|
402
|
+
* @returns array of all the graph models in the project.
|
|
403
|
+
*/
|
|
404
|
+
public async graphModels(
|
|
405
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
406
|
+
): Promise<Resource[]> {
|
|
407
|
+
return this.resources.resources('graphModels', from);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Returns an array of all the graph views in the project.
|
|
412
|
+
* @param from Defines where resources are collected from.
|
|
413
|
+
* @returns array of all the graph views in the project.
|
|
414
|
+
*/
|
|
415
|
+
public async graphViews(
|
|
416
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
417
|
+
): Promise<Resource[]> {
|
|
418
|
+
return this.resources.resources('graphViews', from);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Checks if a given card is part of this project.
|
|
423
|
+
* @param cardKey card to check.
|
|
424
|
+
* @returns true if a given card is found from project, false otherwise.
|
|
425
|
+
*/
|
|
426
|
+
public hasCard(cardKey: string): boolean {
|
|
427
|
+
return super.hasCard(cardKey, this.paths.cardRootFolder);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Adds a module from project.
|
|
432
|
+
* @param moduleName Name of the module
|
|
433
|
+
*/
|
|
434
|
+
public async importModule(module: ModuleSetting) {
|
|
435
|
+
// Add module as a dependency.
|
|
436
|
+
await this.configuration.addModule(module);
|
|
437
|
+
await this.collectModuleResources();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Checks if given path is a project.
|
|
442
|
+
* @param path Path to a project
|
|
443
|
+
* @returns true, if in the given path there is a project; false otherwise
|
|
444
|
+
*/
|
|
445
|
+
static isCreated(path: string): boolean {
|
|
446
|
+
return pathExists(join(path, 'cardRoot'));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Returns whether card is a template card or not
|
|
451
|
+
* @param cardKey card to check.
|
|
452
|
+
* @todo: This is only used from 'remove'. Could it use the static checker?
|
|
453
|
+
*/
|
|
454
|
+
public async isTemplateCard(cardKey: string): Promise<boolean> {
|
|
455
|
+
const templateCards = await this.allTemplateCards();
|
|
456
|
+
return templateCards.find((card) => card.key === cardKey) != null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Returns an array of all the link types in the project.
|
|
461
|
+
* @param from Defines where resources are collected from.
|
|
462
|
+
* @returns array of all link types in the project.
|
|
463
|
+
*/
|
|
464
|
+
public async linkTypes(
|
|
465
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
466
|
+
): Promise<Resource[]> {
|
|
467
|
+
return this.resources.resources('linkTypes', from);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Returns an array of cards in the project, in the templates or both.
|
|
472
|
+
* Cards don't have content and nor metadata.
|
|
473
|
+
* @param includeCardsFrom Where to return cards from (project, templates, or both)
|
|
474
|
+
* @returns all cards in the project.
|
|
475
|
+
*/
|
|
476
|
+
public async listCards(
|
|
477
|
+
cardsFrom: CardLocation = CardLocation.all,
|
|
478
|
+
): Promise<CardListContainer[]> {
|
|
479
|
+
const cardListContainer: CardListContainer[] = [];
|
|
480
|
+
if (
|
|
481
|
+
cardsFrom === CardLocation.all ||
|
|
482
|
+
cardsFrom === CardLocation.projectOnly
|
|
483
|
+
) {
|
|
484
|
+
const projectCards = (await super.cards(this.paths.cardRootFolder)).map(
|
|
485
|
+
(item) => item.key,
|
|
486
|
+
);
|
|
487
|
+
cardListContainer.push({
|
|
488
|
+
name: this.projectName,
|
|
489
|
+
type: 'project',
|
|
490
|
+
cards: projectCards,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (
|
|
495
|
+
cardsFrom === CardLocation.all ||
|
|
496
|
+
cardsFrom === CardLocation.templatesOnly
|
|
497
|
+
) {
|
|
498
|
+
const templates = await this.templates();
|
|
499
|
+
for (const template of templates) {
|
|
500
|
+
const templateObject = new TemplateResource(
|
|
501
|
+
this,
|
|
502
|
+
resourceName(template.name),
|
|
503
|
+
).templateObject();
|
|
504
|
+
if (templateObject) {
|
|
505
|
+
// todo: optimization - do all this in parallel
|
|
506
|
+
const templateCards = await templateObject.listCards();
|
|
507
|
+
if (templateCards) {
|
|
508
|
+
cardListContainer.push({
|
|
509
|
+
name: template.name,
|
|
510
|
+
type: 'template',
|
|
511
|
+
cards: templateCards.map((item) => item.key),
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return cardListContainer;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Return cardIDs of the cards in the project or from templates, or both.
|
|
522
|
+
* @param includeCardsFrom Where to return cards from (project, templates, or both)
|
|
523
|
+
* @returns Array of cardIDs.
|
|
524
|
+
* @note that cardIDs are not sorted.
|
|
525
|
+
*/
|
|
526
|
+
public async listCardIds(
|
|
527
|
+
cardsFrom: CardLocation = CardLocation.all,
|
|
528
|
+
): Promise<Set<string>> {
|
|
529
|
+
const promises: Promise<Set<string>>[] = [];
|
|
530
|
+
if (
|
|
531
|
+
cardsFrom === CardLocation.all ||
|
|
532
|
+
cardsFrom === CardLocation.projectOnly
|
|
533
|
+
) {
|
|
534
|
+
promises.push(
|
|
535
|
+
super
|
|
536
|
+
.cards(this.paths.cardRootFolder)
|
|
537
|
+
.then((cards) => new Set(cards.map((card) => card.key))),
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
if (
|
|
541
|
+
cardsFrom === CardLocation.all ||
|
|
542
|
+
cardsFrom === CardLocation.templatesOnly
|
|
543
|
+
) {
|
|
544
|
+
promises.push(
|
|
545
|
+
this.templates().then((templates) =>
|
|
546
|
+
Promise.allSettled(
|
|
547
|
+
templates.map(
|
|
548
|
+
(template) =>
|
|
549
|
+
new TemplateResource(this, resourceName(template.name)),
|
|
550
|
+
),
|
|
551
|
+
)
|
|
552
|
+
.then((results) =>
|
|
553
|
+
results
|
|
554
|
+
.filter(
|
|
555
|
+
(
|
|
556
|
+
result,
|
|
557
|
+
): result is PromiseFulfilledResult<TemplateResource> =>
|
|
558
|
+
result.status === 'fulfilled' && result.value !== null,
|
|
559
|
+
)
|
|
560
|
+
.map((result) => result.value),
|
|
561
|
+
)
|
|
562
|
+
.then((templateObjects) =>
|
|
563
|
+
Promise.allSettled(
|
|
564
|
+
templateObjects.map((obj) => obj.templateObject().listCards()),
|
|
565
|
+
),
|
|
566
|
+
)
|
|
567
|
+
.then((results) => {
|
|
568
|
+
const templateCardIds = new Set<string>();
|
|
569
|
+
results
|
|
570
|
+
.filter(
|
|
571
|
+
(result): result is PromiseFulfilledResult<Card[]> =>
|
|
572
|
+
result.status === 'fulfilled',
|
|
573
|
+
)
|
|
574
|
+
.forEach((result) => {
|
|
575
|
+
result.value.forEach((card) => templateCardIds.add(card.key));
|
|
576
|
+
});
|
|
577
|
+
return templateCardIds;
|
|
578
|
+
}),
|
|
579
|
+
),
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
const allCardIdSets = await Promise.all(promises);
|
|
583
|
+
return new Set(allCardIdSets.flatMap((set) => [...set]));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Returns details of a certain module.
|
|
588
|
+
* @param moduleName Name of the module.
|
|
589
|
+
* @returns module details, or undefined if workflow cannot be found.
|
|
590
|
+
*/
|
|
591
|
+
public async module(moduleName: string): Promise<ModuleContent | undefined> {
|
|
592
|
+
const module = await this.findModule(moduleName);
|
|
593
|
+
if (module && module.path) {
|
|
594
|
+
const modulePath = join(module.path, module.name);
|
|
595
|
+
const moduleConfig = (await readJsonFile(
|
|
596
|
+
join(modulePath, Project.projectConfigFileName),
|
|
597
|
+
)) as ModuleContent;
|
|
598
|
+
return {
|
|
599
|
+
name: moduleConfig.name,
|
|
600
|
+
modules: moduleConfig.modules,
|
|
601
|
+
path: modulePath,
|
|
602
|
+
cardKeyPrefix: moduleConfig.cardKeyPrefix,
|
|
603
|
+
calculations: [
|
|
604
|
+
...(await this.resources.collectResourcesFromModules('calculations')),
|
|
605
|
+
],
|
|
606
|
+
cardTypes: [
|
|
607
|
+
...(await this.resources.collectResourcesFromModules('cardTypes')),
|
|
608
|
+
],
|
|
609
|
+
fieldTypes: [
|
|
610
|
+
...(await this.resources.collectResourcesFromModules('fieldTypes')),
|
|
611
|
+
],
|
|
612
|
+
graphModels: [
|
|
613
|
+
...(await this.resources.collectResourcesFromModules('graphModels')),
|
|
614
|
+
],
|
|
615
|
+
graphViews: [
|
|
616
|
+
...(await this.resources.collectResourcesFromModules('graphViews')),
|
|
617
|
+
],
|
|
618
|
+
linkTypes: [
|
|
619
|
+
...(await this.resources.collectResourcesFromModules('linkTypes')),
|
|
620
|
+
],
|
|
621
|
+
reports: [
|
|
622
|
+
...(await this.resources.collectResourcesFromModules('reports')),
|
|
623
|
+
],
|
|
624
|
+
templates: [
|
|
625
|
+
...(await this.resources.collectResourcesFromModules('templates')),
|
|
626
|
+
],
|
|
627
|
+
workflows: [
|
|
628
|
+
...(await this.resources.collectResourcesFromModules('workflows')),
|
|
629
|
+
],
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return undefined;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Returns list of modules in the project.
|
|
637
|
+
* @returns list of modules in the project.
|
|
638
|
+
*/
|
|
639
|
+
public async modules(): Promise<Resource[]> {
|
|
640
|
+
return this.resources.resources('modules');
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Returns a new unique card key with project prefix (e.g. test_x649it4x).
|
|
645
|
+
* Random part of string will be always 8 characters in base-36 (0-9a-z)
|
|
646
|
+
* @returns a new card key string
|
|
647
|
+
* @throws if a unique key could not be created within set number of attempts
|
|
648
|
+
*/
|
|
649
|
+
public newCardKey(cardIds: Set<string>): string {
|
|
650
|
+
const maxAttempts = 10;
|
|
651
|
+
const base = 36;
|
|
652
|
+
const length = 8;
|
|
653
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
654
|
+
// Create a key and check that there are no collisions with other keys in project
|
|
655
|
+
const newKey = `${this.settings.cardKeyPrefix}_${generateRandomString(base, length)}`;
|
|
656
|
+
if (cardIds.has(newKey)) {
|
|
657
|
+
continue;
|
|
658
|
+
} else {
|
|
659
|
+
cardIds.add(newKey);
|
|
660
|
+
}
|
|
661
|
+
return newKey;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
throw 'Could not generate unique card key';
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Returns an array of new unique card keys with project prefix (e.g. test_x649it4x).
|
|
669
|
+
* Random part of string will be always 8 characters in base-36 (0-9a-z)
|
|
670
|
+
* @returns an array of new card key strings
|
|
671
|
+
* @throws if a unique key could not be created within set number of attempts
|
|
672
|
+
*/
|
|
673
|
+
public newCardKeys(keysToCreate: number, cardIds: Set<string>): string[] {
|
|
674
|
+
if (keysToCreate < 1) {
|
|
675
|
+
return [];
|
|
676
|
+
}
|
|
677
|
+
const createdKeys: string[] = [];
|
|
678
|
+
const base = 36;
|
|
679
|
+
const length = 8;
|
|
680
|
+
let maxAttempts = 10 * keysToCreate;
|
|
681
|
+
while (true) {
|
|
682
|
+
if (maxAttempts <= 0) {
|
|
683
|
+
throw new Error('Could not generate unique card key');
|
|
684
|
+
}
|
|
685
|
+
const newKey = `${this.settings.cardKeyPrefix}_${generateRandomString(base, length)}`;
|
|
686
|
+
if (cardIds.has(newKey)) {
|
|
687
|
+
--maxAttempts;
|
|
688
|
+
continue;
|
|
689
|
+
} else {
|
|
690
|
+
cardIds.add(newKey);
|
|
691
|
+
createdKeys.push(newKey);
|
|
692
|
+
}
|
|
693
|
+
if (createdKeys.length >= keysToCreate) {
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return createdKeys;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Getter. Returns a class that handles the project's paths.
|
|
702
|
+
*/
|
|
703
|
+
public get paths(): ProjectPaths {
|
|
704
|
+
return this.projectPaths;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Returns full path to a given card.
|
|
709
|
+
* @param cardKey card to check path for.
|
|
710
|
+
* @returns path to a given card.
|
|
711
|
+
*/
|
|
712
|
+
public pathToCard(cardKey: string): string {
|
|
713
|
+
const allFiles = getFilesSync(this.paths.cardRootFolder);
|
|
714
|
+
const cardIndexJsonFile = join(cardKey, Project.cardMetadataFile);
|
|
715
|
+
const foundFile = allFiles.find((file) => file.includes(cardIndexJsonFile));
|
|
716
|
+
return foundFile ? dirname(foundFile) : '';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Getter. Returns project name.
|
|
721
|
+
*/
|
|
722
|
+
public get projectName(): string {
|
|
723
|
+
return this.settings.name;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Getter. Returns project prefix.
|
|
728
|
+
*/
|
|
729
|
+
public get projectPrefix(): string {
|
|
730
|
+
return this.settings.cardKeyPrefix;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Collects all prefixes used in the project (project's own plus all from modules).
|
|
735
|
+
* @returns all prefixes used in the project.
|
|
736
|
+
* @todo - move the module prefix fetch to resource-collector.
|
|
737
|
+
* Make it use cached value that is only changed when module is removed/imported.
|
|
738
|
+
*/
|
|
739
|
+
public async projectPrefixes(): Promise<string[]> {
|
|
740
|
+
const prefixes: string[] = [this.projectPrefix];
|
|
741
|
+
let files;
|
|
742
|
+
try {
|
|
743
|
+
files = await readdir(this.paths.modulesFolder, {
|
|
744
|
+
withFileTypes: true,
|
|
745
|
+
recursive: true,
|
|
746
|
+
});
|
|
747
|
+
const configurationFiles = files
|
|
748
|
+
.filter((dirent) => dirent.isFile())
|
|
749
|
+
.filter((dirent) => dirent.name === Project.projectConfigFileName);
|
|
750
|
+
|
|
751
|
+
const configurationPromises = configurationFiles.map(async (file) => {
|
|
752
|
+
const configuration = (await readJsonFile(
|
|
753
|
+
join(file.parentPath, file.name),
|
|
754
|
+
)) as ProjectSettings;
|
|
755
|
+
return configuration.cardKeyPrefix;
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const configurationPrefixes = await Promise.all(configurationPromises);
|
|
759
|
+
prefixes.push(...configurationPrefixes);
|
|
760
|
+
} catch {
|
|
761
|
+
// do nothing if readdir throws
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return prefixes;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Removes a module from project.
|
|
769
|
+
* @param moduleName Name of the module
|
|
770
|
+
*/
|
|
771
|
+
public async removeModule(moduleName: string) {
|
|
772
|
+
await this.configuration.removeModule(moduleName);
|
|
773
|
+
await this.collectModuleResources();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Array of reports in the project.
|
|
778
|
+
* @param from Defines where resources are collected from.
|
|
779
|
+
* @returns array of all reports in the project.
|
|
780
|
+
*/
|
|
781
|
+
public async reports(
|
|
782
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
783
|
+
): Promise<Resource[]> {
|
|
784
|
+
return this.resources.resources('reports', from);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Returns handlebar files from reports.
|
|
789
|
+
* @param from Defines where report handlebar files are collected from.
|
|
790
|
+
* @returns handlebar files from reports.
|
|
791
|
+
*/
|
|
792
|
+
public async reportHandlerBarFiles(from: ResourcesFrom = ResourcesFrom.all) {
|
|
793
|
+
const reports = await this.reports(from);
|
|
794
|
+
const handleBarFiles: string[] = [];
|
|
795
|
+
for (const reportResourceName of reports) {
|
|
796
|
+
const name = resourceName(reportResourceName.name);
|
|
797
|
+
const report = new ReportResource(this, name);
|
|
798
|
+
handleBarFiles.push(...(await report.handleBarFiles()));
|
|
799
|
+
}
|
|
800
|
+
return handleBarFiles;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Removes a resource from Project.
|
|
805
|
+
* @param resource Resource to remove.
|
|
806
|
+
*/
|
|
807
|
+
public removeResource(resource: Resource) {
|
|
808
|
+
this.resources.remove(resource);
|
|
809
|
+
this.createdResources.delete(resource.name);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Returns metadata from a given resource
|
|
814
|
+
* @param name Name of a resource
|
|
815
|
+
* @returns Metadata from the resource.
|
|
816
|
+
*/
|
|
817
|
+
public async resource<Type>(name: string): Promise<Type | undefined> {
|
|
818
|
+
const resName = resourceName(name);
|
|
819
|
+
if (this.createdResources.has(resourceNameToString(resName))) {
|
|
820
|
+
const value = this.createdResources.get(
|
|
821
|
+
resourceNameToString(resName),
|
|
822
|
+
) as unknown as Type;
|
|
823
|
+
return value;
|
|
824
|
+
}
|
|
825
|
+
let resource = undefined;
|
|
826
|
+
try {
|
|
827
|
+
resource = Project.resourceObject(this, resName);
|
|
828
|
+
} catch {
|
|
829
|
+
return undefined;
|
|
830
|
+
}
|
|
831
|
+
const data = resource?.data as Type;
|
|
832
|
+
if (!data) {
|
|
833
|
+
return undefined;
|
|
834
|
+
}
|
|
835
|
+
return data;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
public get resourceCache(): Map<string, JSON> {
|
|
839
|
+
return this.createdResources;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Checks if a given resource exists in the project already.
|
|
844
|
+
* @param resourceType Type of resource as a string.
|
|
845
|
+
* @param name Valid name of resource.
|
|
846
|
+
* @returns boolean, true if resource exists; false otherwise.
|
|
847
|
+
*/
|
|
848
|
+
public async resourceExists(
|
|
849
|
+
resourceType: ResourceFolderType,
|
|
850
|
+
name: string,
|
|
851
|
+
): Promise<boolean> {
|
|
852
|
+
const resources = await this.resourcesOfType(
|
|
853
|
+
resourceType,
|
|
854
|
+
ResourcesFrom.all,
|
|
855
|
+
);
|
|
856
|
+
const resource = resources.find((item) => item.name === name);
|
|
857
|
+
return resource !== undefined;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Instantiates resource object from project with a resource name.
|
|
862
|
+
* @note that this is memory based object only.
|
|
863
|
+
* To manipulate the resource (create files, delete files etc), use the resource object's API.
|
|
864
|
+
* @param project Project from which resources are created from.
|
|
865
|
+
* @param name Resource name
|
|
866
|
+
* @throws if called with unsupported resource type.
|
|
867
|
+
* @returns Created resource.
|
|
868
|
+
*/
|
|
869
|
+
public static resourceObject(project: Project, name: ResourceName) {
|
|
870
|
+
if (name.type === 'cardTypes') {
|
|
871
|
+
return new CardTypeResource(project, name);
|
|
872
|
+
} else if (name.type === 'fieldTypes') {
|
|
873
|
+
return new FieldTypeResource(project, name);
|
|
874
|
+
} else if (name.type === 'graphModels') {
|
|
875
|
+
return new GraphModelResource(project, name);
|
|
876
|
+
} else if (name.type === 'graphViews') {
|
|
877
|
+
return new GraphViewResource(project, name);
|
|
878
|
+
} else if (name.type === 'linkTypes') {
|
|
879
|
+
return new LinkTypeResource(project, name);
|
|
880
|
+
} else if (name.type === 'reports') {
|
|
881
|
+
return new ReportResource(project, name);
|
|
882
|
+
} else if (name.type === 'templates') {
|
|
883
|
+
return new TemplateResource(project, name);
|
|
884
|
+
} else if (name.type === 'workflows') {
|
|
885
|
+
return new WorkflowResource(project, name);
|
|
886
|
+
}
|
|
887
|
+
throw new Error(
|
|
888
|
+
`Unsupported resource type '${resourceNameToString(name)}'`,
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Shows details of a project.
|
|
894
|
+
* @returns details of a project.
|
|
895
|
+
*/
|
|
896
|
+
public async show(): Promise<ProjectMetadata> {
|
|
897
|
+
return {
|
|
898
|
+
name: this.containerName,
|
|
899
|
+
path: this.basePath,
|
|
900
|
+
prefix: this.projectPrefix,
|
|
901
|
+
modules: (await this.modules()).map((item) => item.name),
|
|
902
|
+
numberOfCards: (await this.listCards(CardLocation.projectOnly))[0].cards
|
|
903
|
+
.length,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Show cards of a project.
|
|
909
|
+
* @returns an array of all project cards in the project.
|
|
910
|
+
*/
|
|
911
|
+
public async showProjectCards(): Promise<Card[]> {
|
|
912
|
+
const cards: Card[] = [];
|
|
913
|
+
const cardPathMap = new Map<string, Card>();
|
|
914
|
+
const entries = await readdir(this.paths.cardRootFolder, {
|
|
915
|
+
withFileTypes: true,
|
|
916
|
+
recursive: true,
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
// Checks if Dirent folder is a card folder
|
|
920
|
+
function cardFolder(
|
|
921
|
+
entry: Dirent,
|
|
922
|
+
cardPathMap: Map<string, Card>,
|
|
923
|
+
): Card | undefined {
|
|
924
|
+
const fullPath = join(entry.parentPath, entry.name);
|
|
925
|
+
if (!cardPathMap.has(fullPath)) {
|
|
926
|
+
const newCard: Card = {
|
|
927
|
+
key: entry.name,
|
|
928
|
+
path: fullPath,
|
|
929
|
+
children: [],
|
|
930
|
+
attachments: [],
|
|
931
|
+
};
|
|
932
|
+
cardPathMap.set(fullPath, newCard);
|
|
933
|
+
return newCard;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Process card directories first
|
|
938
|
+
entries
|
|
939
|
+
.filter((entry) => entry.isDirectory() && CardNameRegEx.test(entry.name))
|
|
940
|
+
.forEach((entry) => {
|
|
941
|
+
const card = cardFolder(entry, cardPathMap);
|
|
942
|
+
if (card) cards.push(card);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// Process metadata files in parallel
|
|
946
|
+
await Promise.all(
|
|
947
|
+
entries
|
|
948
|
+
.filter(
|
|
949
|
+
(entry) => entry.isFile() && entry.name === Project.cardMetadataFile,
|
|
950
|
+
)
|
|
951
|
+
.map(async (entry) => {
|
|
952
|
+
const parentCard = cardPathMap.get(entry.parentPath);
|
|
953
|
+
if (!parentCard) return;
|
|
954
|
+
parentCard.metadata = (await readJsonFile(
|
|
955
|
+
join(entry.parentPath, entry.name),
|
|
956
|
+
)) as CardMetadata;
|
|
957
|
+
}),
|
|
958
|
+
);
|
|
959
|
+
|
|
960
|
+
// Finally, build the card hierarchy
|
|
961
|
+
Array.from(cardPathMap.entries()).map(([cardPath, card]) => {
|
|
962
|
+
const parentPath = findParentPath(cardPath);
|
|
963
|
+
if (!parentPath) return;
|
|
964
|
+
const parentCard = cardPathMap.get(parentPath);
|
|
965
|
+
if (!parentCard) return;
|
|
966
|
+
|
|
967
|
+
parentCard.children.push(card);
|
|
968
|
+
const index = cards.indexOf(card);
|
|
969
|
+
if (index > -1) {
|
|
970
|
+
cards.splice(index, 1);
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
return cards;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Returns all template cards from the project. This includes all module templates' cards.
|
|
978
|
+
* @param cardDetails which details to fetch. Optional.
|
|
979
|
+
* @returns all the template cards from the project
|
|
980
|
+
*/
|
|
981
|
+
public async allTemplateCards(
|
|
982
|
+
cardDetails?: FetchCardDetails,
|
|
983
|
+
): Promise<Card[]> {
|
|
984
|
+
const templates = await this.templates();
|
|
985
|
+
const cards: Card[] = [];
|
|
986
|
+
for (const template of templates) {
|
|
987
|
+
const templateCards = await this.templateCards(
|
|
988
|
+
template.name,
|
|
989
|
+
cardDetails,
|
|
990
|
+
);
|
|
991
|
+
if (templateCards) cards.push(...templateCards);
|
|
992
|
+
}
|
|
993
|
+
return cards;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Returns cards from single template.
|
|
998
|
+
* @param templateName Name of the template
|
|
999
|
+
* @param cardDetails Card information
|
|
1000
|
+
* @returns List of cards from template.
|
|
1001
|
+
*/
|
|
1002
|
+
public async templateCards(
|
|
1003
|
+
templateName: string,
|
|
1004
|
+
cardDetails?: FetchCardDetails,
|
|
1005
|
+
): Promise<Card[]> {
|
|
1006
|
+
const templateObject = new TemplateResource(
|
|
1007
|
+
this,
|
|
1008
|
+
resourceName(templateName),
|
|
1009
|
+
).templateObject();
|
|
1010
|
+
return await templateObject?.cards('', cardDetails);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Array of templates in the project.
|
|
1015
|
+
* @param from Defines where resources are collected from.
|
|
1016
|
+
* @returns array of all templates in the project.
|
|
1017
|
+
*/
|
|
1018
|
+
public async templates(
|
|
1019
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
1020
|
+
): Promise<Resource[]> {
|
|
1021
|
+
return this.resources.resources('templates', from);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Update card content.
|
|
1026
|
+
* @param cardKey card's ID that is updated.
|
|
1027
|
+
* @param content changed content
|
|
1028
|
+
*/
|
|
1029
|
+
public async updateCardContent(cardKey: string, content: string) {
|
|
1030
|
+
const card = await this.findCard(this.basePath, cardKey, {
|
|
1031
|
+
metadata: true,
|
|
1032
|
+
content: true,
|
|
1033
|
+
});
|
|
1034
|
+
if (!card) {
|
|
1035
|
+
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
1036
|
+
}
|
|
1037
|
+
card.content = content;
|
|
1038
|
+
await this.saveCard(card);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Updates card metadata's single key.
|
|
1043
|
+
* @param cardKey card that is updated.
|
|
1044
|
+
* @param changedKey changed metadata key
|
|
1045
|
+
* @param newValue changed value for the key
|
|
1046
|
+
*/
|
|
1047
|
+
public async updateCardMetadataKey(
|
|
1048
|
+
cardKey: string,
|
|
1049
|
+
changedKey: string,
|
|
1050
|
+
newValue: MetadataContent,
|
|
1051
|
+
) {
|
|
1052
|
+
const templateCard = await this.isTemplateCard(cardKey);
|
|
1053
|
+
const card = await this.findCard(
|
|
1054
|
+
templateCard ? this.paths.templatesFolder : this.paths.cardRootFolder,
|
|
1055
|
+
cardKey,
|
|
1056
|
+
{
|
|
1057
|
+
metadata: true,
|
|
1058
|
+
},
|
|
1059
|
+
);
|
|
1060
|
+
if (!card) {
|
|
1061
|
+
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (!card.metadata || card.metadata[changedKey] === newValue) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
const cardAsRecord: Record<string, MetadataContent> = card.metadata;
|
|
1068
|
+
cardAsRecord[changedKey] = newValue;
|
|
1069
|
+
|
|
1070
|
+
const invalidCard = isTemplateCard(card)
|
|
1071
|
+
? ''
|
|
1072
|
+
: await this.validateCard(card);
|
|
1073
|
+
if (invalidCard.length !== 0) {
|
|
1074
|
+
throw new Error(invalidCard);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
await this.saveCardMetadata(card);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Updates card metadata.
|
|
1082
|
+
* @param card affected card
|
|
1083
|
+
* @param changedMetadata changed content for the card
|
|
1084
|
+
*/
|
|
1085
|
+
public async updateCardMetadata(card: Card, changedMetadata: CardMetadata) {
|
|
1086
|
+
card.metadata = changedMetadata;
|
|
1087
|
+
return this.saveCardMetadata(card);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Validates that card's data is valid.
|
|
1092
|
+
* @param card Card to validate.
|
|
1093
|
+
*/
|
|
1094
|
+
public async validateCard(card: Card): Promise<string> {
|
|
1095
|
+
const invalidCustomData = await this.validator.validateCustomFields(
|
|
1096
|
+
this,
|
|
1097
|
+
card,
|
|
1098
|
+
);
|
|
1099
|
+
const invalidWorkFlow = await this.validator.validateWorkflowState(
|
|
1100
|
+
this,
|
|
1101
|
+
card,
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
const invalidLabels = await this.validator.validateCardLabels(card);
|
|
1105
|
+
if (
|
|
1106
|
+
invalidCustomData.length === 0 &&
|
|
1107
|
+
invalidWorkFlow.length === 0 &&
|
|
1108
|
+
invalidLabels.length === 0
|
|
1109
|
+
) {
|
|
1110
|
+
return '';
|
|
1111
|
+
}
|
|
1112
|
+
const errors: string[] = [];
|
|
1113
|
+
if (invalidCustomData.length > 0) {
|
|
1114
|
+
errors.push(invalidCustomData);
|
|
1115
|
+
}
|
|
1116
|
+
if (invalidWorkFlow.length > 0) {
|
|
1117
|
+
errors.push(invalidWorkFlow);
|
|
1118
|
+
}
|
|
1119
|
+
if (invalidLabels.length > 0) {
|
|
1120
|
+
errors.push(invalidLabels);
|
|
1121
|
+
}
|
|
1122
|
+
return errors.join('\n');
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Array of workflows in the project.
|
|
1127
|
+
* @param from Defines where resources are collected from.
|
|
1128
|
+
* @returns array of all workflows in the project.
|
|
1129
|
+
*/
|
|
1130
|
+
public async workflows(
|
|
1131
|
+
from: ResourcesFrom = ResourcesFrom.all,
|
|
1132
|
+
): Promise<Resource[]> {
|
|
1133
|
+
return this.resources.resources('workflows', from);
|
|
1134
|
+
}
|
|
1135
|
+
}
|