@cyberismo/data-handler 0.0.13 → 0.0.15
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/dist/card-metadata-updater.js +1 -3
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.js +13 -17
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +1 -1
- package/dist/command-manager.js +4 -3
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.d.ts +3 -3
- package/dist/commands/create.js +20 -81
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.d.ts +12 -25
- package/dist/commands/edit.js +25 -74
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/export.js +4 -17
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/fetch.js +2 -1
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/import.js +3 -5
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/move.d.ts +1 -2
- package/dist/commands/move.js +108 -146
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.js +15 -49
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.d.ts +1 -0
- package/dist/commands/rename.js +13 -7
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +7 -25
- package/dist/commands/show.js +39 -113
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.js +27 -30
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.d.ts +5 -3
- package/dist/commands/update.js +19 -5
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +3 -3
- package/dist/commands/validate.js +20 -27
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.d.ts +87 -24
- package/dist/containers/card-container.js +183 -279
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/calculation-engine.d.ts +6 -0
- package/dist/containers/project/calculation-engine.js +36 -29
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project/card-cache.d.ts +146 -0
- package/dist/containers/project/card-cache.js +411 -0
- package/dist/containers/project/card-cache.js.map +1 -0
- package/dist/containers/project/resource-collector.d.ts +24 -1
- package/dist/containers/project/resource-collector.js +8 -1
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +119 -84
- package/dist/containers/project.js +423 -253
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +15 -31
- package/dist/containers/template.js +97 -104
- package/dist/containers/template.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/folder-content-interfaces.d.ts +12 -5
- package/dist/interfaces/folder-content-interfaces.js +5 -3
- package/dist/interfaces/folder-content-interfaces.js.map +1 -1
- package/dist/interfaces/macros.d.ts +1 -0
- package/dist/interfaces/macros.js +1 -1
- package/dist/interfaces/macros.js.map +1 -1
- package/dist/interfaces/project-interfaces.d.ts +16 -10
- package/dist/interfaces/project-interfaces.js +10 -8
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +21 -22
- package/dist/interfaces/resource-interfaces.js +3 -0
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/common.d.ts +10 -10
- package/dist/macros/createCards/index.d.ts +0 -13
- package/dist/macros/createCards/index.js.map +1 -1
- package/dist/macros/createCards/types.d.ts +44 -0
- package/dist/macros/createCards/types.js +15 -0
- package/dist/macros/createCards/types.js.map +1 -0
- package/dist/macros/graph/index.d.ts +2 -6
- package/dist/macros/graph/index.js +2 -2
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/graph/types.d.ts +23 -0
- package/dist/macros/graph/types.js +15 -0
- package/dist/macros/graph/types.js.map +1 -0
- package/dist/macros/image/index.d.ts +8 -16
- package/dist/macros/image/index.js +36 -33
- package/dist/macros/image/index.js.map +1 -1
- package/dist/macros/image/types.d.ts +38 -0
- package/dist/macros/image/types.js +15 -0
- package/dist/macros/image/types.js.map +1 -0
- package/dist/macros/include/index.d.ts +1 -6
- package/dist/macros/include/index.js +4 -7
- package/dist/macros/include/index.js.map +1 -1
- package/dist/macros/include/types.d.ts +31 -0
- package/dist/macros/include/types.js +15 -0
- package/dist/macros/include/types.js.map +1 -0
- package/dist/macros/percentage/index.d.ts +0 -6
- package/dist/macros/percentage/index.js.map +1 -1
- package/dist/macros/percentage/types.d.ts +31 -0
- package/dist/macros/percentage/types.js +15 -0
- package/dist/macros/percentage/types.js.map +1 -0
- package/dist/macros/report/index.d.ts +0 -3
- package/dist/macros/report/index.js.map +1 -1
- package/dist/macros/report/types.d.ts +19 -0
- package/dist/macros/report/types.js +15 -0
- package/dist/macros/report/types.js.map +1 -0
- package/dist/macros/scoreCard/index.d.ts +0 -6
- package/dist/macros/scoreCard/index.js.map +1 -1
- package/dist/macros/scoreCard/types.d.ts +31 -0
- package/dist/macros/scoreCard/types.js +15 -0
- package/dist/macros/scoreCard/types.js.map +1 -0
- package/dist/macros/types.d.ts +25 -0
- package/dist/macros/types.js +2 -0
- package/dist/macros/types.js.map +1 -0
- package/dist/macros/vega/index.d.ts +0 -4
- package/dist/macros/vega/index.js.map +1 -1
- package/dist/macros/vega/types.d.ts +20 -0
- package/dist/macros/vega/types.js +2 -0
- package/dist/macros/vega/types.js.map +1 -0
- package/dist/macros/vegalite/index.d.ts +0 -4
- package/dist/macros/vegalite/index.js.map +1 -1
- package/dist/macros/vegalite/types.d.ts +20 -0
- package/dist/macros/vegalite/types.js +15 -0
- package/dist/macros/vegalite/types.js.map +1 -0
- package/dist/macros/xref/index.d.ts +0 -3
- package/dist/macros/xref/index.js +5 -14
- package/dist/macros/xref/index.js.map +1 -1
- package/dist/macros/xref/types.d.ts +19 -0
- package/dist/macros/xref/types.js +15 -0
- package/dist/macros/xref/types.js.map +1 -0
- package/dist/module-manager.js +4 -4
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +43 -0
- package/dist/resources/calculation-resource.js +75 -0
- package/dist/resources/calculation-resource.js.map +1 -0
- package/dist/resources/card-type-resource.d.ts +4 -21
- package/dist/resources/card-type-resource.js +13 -44
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/create-defaults.d.ts +13 -6
- package/dist/resources/create-defaults.js +19 -5
- package/dist/resources/create-defaults.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +4 -21
- package/dist/resources/field-type-resource.js +14 -38
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +12 -29
- package/dist/resources/file-resource.js +19 -287
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +32 -51
- package/dist/resources/folder-resource.js +68 -96
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +5 -33
- package/dist/resources/graph-model-resource.js +8 -61
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +5 -28
- package/dist/resources/graph-view-resource.js +6 -45
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +4 -21
- package/dist/resources/link-type-resource.js +6 -31
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +5 -17
- package/dist/resources/report-resource.js +6 -44
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +58 -23
- package/dist/resources/resource-object.js +307 -26
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +4 -15
- package/dist/resources/template-resource.js +10 -25
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +4 -23
- package/dist/resources/workflow-resource.js +12 -38
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/card-utils.d.ts +69 -19
- package/dist/utils/card-utils.js +179 -30
- package/dist/utils/card-utils.js.map +1 -1
- package/dist/utils/clingo-facts.js +11 -3
- package/dist/utils/clingo-facts.js.map +1 -1
- package/dist/utils/clingo-parser.js +1 -1
- package/dist/utils/clingo-parser.js.map +1 -1
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/csv.js +1 -1
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/error-utils.d.ts +34 -0
- package/dist/utils/error-utils.js +56 -0
- package/dist/utils/error-utils.js.map +1 -0
- package/dist/utils/log-utils.d.ts +0 -27
- package/dist/utils/log-utils.js +0 -58
- package/dist/utils/log-utils.js.map +1 -1
- package/dist/utils/user-preferences.js +6 -3
- package/dist/utils/user-preferences.js.map +1 -1
- package/package.json +5 -5
- package/src/card-metadata-updater.ts +3 -5
- package/src/command-handler.ts +14 -19
- package/src/command-manager.ts +4 -3
- package/src/commands/create.ts +28 -112
- package/src/commands/edit.ts +27 -118
- package/src/commands/export.ts +8 -29
- package/src/commands/fetch.ts +2 -1
- package/src/commands/import.ts +4 -6
- package/src/commands/move.ts +144 -179
- package/src/commands/remove.ts +12 -54
- package/src/commands/rename.ts +22 -7
- package/src/commands/show.ts +51 -156
- package/src/commands/transition.ts +30 -33
- package/src/commands/update.ts +27 -9
- package/src/commands/validate.ts +22 -37
- package/src/containers/card-container.ts +200 -360
- package/src/containers/project/calculation-engine.ts +43 -33
- package/src/containers/project/card-cache.ts +497 -0
- package/src/containers/project/resource-collector.ts +9 -1
- package/src/containers/project.ts +533 -328
- package/src/containers/template.ts +109 -127
- package/src/index.ts +1 -0
- package/src/interfaces/folder-content-interfaces.ts +23 -5
- package/src/interfaces/macros.ts +2 -0
- package/src/interfaces/project-interfaces.ts +19 -10
- package/src/interfaces/resource-interfaces.ts +22 -24
- package/src/macros/createCards/index.ts +1 -12
- package/src/macros/createCards/types.ts +46 -0
- package/src/macros/graph/index.ts +3 -7
- package/src/macros/graph/types.ts +24 -0
- package/src/macros/image/index.ts +50 -61
- package/src/macros/image/types.ts +39 -0
- package/src/macros/include/index.ts +6 -15
- package/src/macros/include/types.ts +32 -0
- package/src/macros/percentage/index.ts +1 -7
- package/src/macros/percentage/types.ts +32 -0
- package/src/macros/report/index.ts +1 -4
- package/src/macros/report/types.ts +20 -0
- package/src/macros/scoreCard/index.ts +1 -7
- package/src/macros/scoreCard/types.ts +32 -0
- package/src/macros/types.ts +48 -0
- package/src/macros/vega/index.ts +1 -4
- package/src/macros/vega/types.ts +21 -0
- package/src/macros/vegalite/index.ts +1 -4
- package/src/macros/vegalite/types.ts +22 -0
- package/src/macros/xref/index.ts +6 -20
- package/src/macros/xref/types.ts +20 -0
- package/src/module-manager.ts +5 -5
- package/src/project-settings.ts +1 -1
- package/src/resources/calculation-resource.ts +101 -0
- package/src/resources/card-type-resource.ts +24 -59
- package/src/resources/create-defaults.ts +21 -5
- package/src/resources/field-type-resource.ts +22 -51
- package/src/resources/file-resource.ts +27 -403
- package/src/resources/folder-resource.ts +99 -125
- package/src/resources/graph-model-resource.ts +17 -74
- package/src/resources/graph-view-resource.ts +14 -54
- package/src/resources/link-type-resource.ts +13 -40
- package/src/resources/report-resource.ts +17 -57
- package/src/resources/resource-object.ts +454 -39
- package/src/resources/template-resource.ts +16 -29
- package/src/resources/workflow-resource.ts +26 -50
- package/src/utils/card-utils.ts +217 -31
- package/src/utils/clingo-facts.ts +13 -3
- package/src/utils/clingo-parser.ts +1 -1
- package/src/utils/constants.ts +7 -0
- package/src/utils/csv.ts +1 -1
- package/src/utils/error-utils.ts +62 -0
- package/src/utils/log-utils.ts +0 -68
- package/src/utils/user-preferences.ts +7 -3
|
@@ -13,8 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// node
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { basename, join, resolve } from 'node:path';
|
|
17
|
+
import {
|
|
18
|
+
constants as fsConstants,
|
|
19
|
+
copyFile,
|
|
20
|
+
mkdir,
|
|
21
|
+
readdir,
|
|
22
|
+
unlink,
|
|
23
|
+
writeFile,
|
|
24
|
+
} from 'node:fs/promises';
|
|
18
25
|
|
|
19
26
|
import { CardContainer } from './card-container.js'; // base class
|
|
20
27
|
|
|
@@ -35,13 +42,18 @@ import {
|
|
|
35
42
|
type Resource,
|
|
36
43
|
type ResourceFolderType,
|
|
37
44
|
} from '../interfaces/project-interfaces.js';
|
|
38
|
-
import {
|
|
45
|
+
import { pathExists } from '../utils/file-utils.js';
|
|
39
46
|
import { generateRandomString } from '../utils/random.js';
|
|
40
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
cardPathParts,
|
|
49
|
+
isModulePath,
|
|
50
|
+
isTemplateCard,
|
|
51
|
+
} from '../utils/card-utils.js';
|
|
41
52
|
import { ProjectConfiguration } from '../project-settings.js';
|
|
42
53
|
import { ProjectPaths } from './project/project-paths.js';
|
|
43
54
|
import { readJsonFile } from '../utils/json.js';
|
|
44
55
|
import {
|
|
56
|
+
pathToResourceName,
|
|
45
57
|
resourceName,
|
|
46
58
|
type ResourceName,
|
|
47
59
|
resourceNameToString,
|
|
@@ -53,6 +65,7 @@ import {
|
|
|
53
65
|
import type { Template } from './template.js';
|
|
54
66
|
import { Validate } from '../commands/validate.js';
|
|
55
67
|
|
|
68
|
+
import { CalculationResource } from '../resources/calculation-resource.js';
|
|
56
69
|
import { CardTypeResource } from '../resources/card-type-resource.js';
|
|
57
70
|
import { FieldTypeResource } from '../resources/field-type-resource.js';
|
|
58
71
|
import { GraphModelResource } from '../resources/graph-model-resource.js';
|
|
@@ -63,7 +76,9 @@ import { TemplateResource } from '../resources/template-resource.js';
|
|
|
63
76
|
import { WorkflowResource } from '../resources/workflow-resource.js';
|
|
64
77
|
|
|
65
78
|
import { ContentWatcher } from './project/project-content-watcher.js';
|
|
66
|
-
import {
|
|
79
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
80
|
+
|
|
81
|
+
import { ROOT } from '../utils/constants.js';
|
|
67
82
|
|
|
68
83
|
// Re-export this, so that classes that use Project do not need to have separate import.
|
|
69
84
|
export { ResourcesFrom };
|
|
@@ -73,34 +88,44 @@ export { ResourcesFrom };
|
|
|
73
88
|
*/
|
|
74
89
|
export class Project extends CardContainer {
|
|
75
90
|
public calculationEngine: CalculationEngine;
|
|
76
|
-
private resources: ResourceCollector;
|
|
77
|
-
private projectPaths: ProjectPaths;
|
|
78
|
-
private settings: ProjectConfiguration;
|
|
79
|
-
private validator: Validate;
|
|
80
|
-
private resourceWatcher: ContentWatcher | undefined;
|
|
81
|
-
|
|
82
91
|
// Created resources are held in a cache.
|
|
83
92
|
// In the cache, key is resource name, and data is resource metadata (as JSON).
|
|
84
93
|
private createdResources = new Map<string, JSON>();
|
|
94
|
+
private logger = getChildLogger({ module: 'Project' });
|
|
95
|
+
private projectPaths: ProjectPaths;
|
|
96
|
+
private resources: ResourceCollector;
|
|
97
|
+
private resourceWatcher: ContentWatcher | undefined;
|
|
98
|
+
private settings: ProjectConfiguration;
|
|
99
|
+
private validator: Validate;
|
|
85
100
|
|
|
86
101
|
constructor(
|
|
87
102
|
path: string,
|
|
88
103
|
private watchResourceChanges?: boolean,
|
|
89
104
|
) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.calculationEngine = new CalculationEngine(this);
|
|
93
|
-
|
|
94
|
-
this.settings = new ProjectConfiguration(
|
|
105
|
+
const settings = new ProjectConfiguration(
|
|
95
106
|
join(path, '.cards', 'local', Project.projectConfigFileName),
|
|
96
107
|
);
|
|
108
|
+
super(path, settings.cardKeyPrefix, '');
|
|
109
|
+
this.settings = settings;
|
|
110
|
+
|
|
111
|
+
this.logger.info({ path }, 'Initializing project');
|
|
112
|
+
|
|
113
|
+
this.calculationEngine = new CalculationEngine(this);
|
|
97
114
|
this.projectPaths = new ProjectPaths(path);
|
|
98
115
|
this.resources = new ResourceCollector(this);
|
|
99
116
|
|
|
100
117
|
this.containerName = this.settings.name;
|
|
101
118
|
// todo: implement project validation
|
|
102
119
|
this.validator = Validate.getInstance();
|
|
120
|
+
this.logger.info(
|
|
121
|
+
{ resourcesFolder: this.paths.resourcesFolder },
|
|
122
|
+
'Collecting local resources',
|
|
123
|
+
);
|
|
103
124
|
this.resources.collectLocalResources();
|
|
125
|
+
this.logger.info(
|
|
126
|
+
{ name: this.containerName },
|
|
127
|
+
'Project initialization complete',
|
|
128
|
+
);
|
|
104
129
|
|
|
105
130
|
const ignoreRenameFileChanges = true;
|
|
106
131
|
|
|
@@ -134,17 +159,15 @@ export class Project extends CardContainer {
|
|
|
134
159
|
}
|
|
135
160
|
}
|
|
136
161
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (this.createdResources.has(resourceName)) {
|
|
142
|
-
// First, remove the old version from cache
|
|
143
|
-
this.createdResources.delete(resourceName);
|
|
162
|
+
// Changes a card's parent in the cache and updates all relationships.
|
|
163
|
+
private changeParent(updatedCard: Card, previousParent?: string) {
|
|
164
|
+
if (previousParent && previousParent !== ROOT) {
|
|
165
|
+
this.removeCachedChildren(previousParent, updatedCard.key);
|
|
144
166
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
167
|
+
if (updatedCard.parent && updatedCard.parent !== ROOT) {
|
|
168
|
+
this.updateCachedChildren(updatedCard.parent, updatedCard);
|
|
169
|
+
}
|
|
170
|
+
this.cardCache.updateCard(updatedCard.key, updatedCard);
|
|
148
171
|
}
|
|
149
172
|
|
|
150
173
|
// Finds specific module.
|
|
@@ -154,8 +177,55 @@ export class Project extends CardContainer {
|
|
|
154
177
|
);
|
|
155
178
|
}
|
|
156
179
|
|
|
180
|
+
// Handles attachment changes after filesystem operations.
|
|
181
|
+
private async handleAttachmentChange(
|
|
182
|
+
cardKey: string,
|
|
183
|
+
operation: 'added' | 'removed' | 'refresh',
|
|
184
|
+
fileName: string,
|
|
185
|
+
): Promise<void> {
|
|
186
|
+
if (operation === 'added') {
|
|
187
|
+
this.cardCache.addAttachment(cardKey, fileName);
|
|
188
|
+
} else if (operation === 'removed') {
|
|
189
|
+
this.cardCache.deleteAttachment(cardKey, fileName);
|
|
190
|
+
} else if (operation === 'refresh') {
|
|
191
|
+
const newAttachments = this.cardCache.getCardAttachments(cardKey);
|
|
192
|
+
if (newAttachments) {
|
|
193
|
+
this.cardCache.updateCardAttachments(cardKey, newAttachments);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Determines the parent card key from a card's filesystem path.
|
|
199
|
+
private parentFromPath(cardPath: string): string {
|
|
200
|
+
return cardPathParts(this.projectPrefix, cardPath).parents.at(-1) || 'root';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Remove children from a card in the card cache
|
|
204
|
+
private removeCachedChildren(parentKey: string, childKey: string) {
|
|
205
|
+
const parentCard = this.cardCache.getCard(parentKey);
|
|
206
|
+
if (parentCard && parentCard.children) {
|
|
207
|
+
parentCard.children = parentCard.children.filter(
|
|
208
|
+
(child) => child !== childKey,
|
|
209
|
+
);
|
|
210
|
+
this.cardCache.updateCard(parentCard.key, parentCard);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Removes current version of a resource from the resource cache.
|
|
215
|
+
// Then re-creates the resource with current data and caches the value again.
|
|
216
|
+
// If the value wasn't in the cache before, it will be added.
|
|
217
|
+
private async replaceCacheValue(resourceName: string) {
|
|
218
|
+
if (this.createdResources.has(resourceName)) {
|
|
219
|
+
// First, remove the old version from cache
|
|
220
|
+
this.createdResources.delete(resourceName);
|
|
221
|
+
}
|
|
222
|
+
const resourceData = await this.resource(resourceName);
|
|
223
|
+
if (resourceData) {
|
|
224
|
+
this.createdResources.set(resourceName, resourceData as JSON);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
157
228
|
// Returns (local or all) resources of a given type.
|
|
158
|
-
// @todo: if this would be public, we could remove cardTypes(), fieldTypes(), ... and similar APIs
|
|
159
229
|
private async resourcesOfType(
|
|
160
230
|
type: ResourceFolderType,
|
|
161
231
|
from: ResourcesFrom = ResourcesFrom.localOnly,
|
|
@@ -163,6 +233,81 @@ export class Project extends CardContainer {
|
|
|
163
233
|
return this.resources.resources(type, from);
|
|
164
234
|
}
|
|
165
235
|
|
|
236
|
+
// Updates children in the card cache
|
|
237
|
+
private updateCachedChildren(parentKey: string, newChild: Card) {
|
|
238
|
+
const parentCard = this.cardCache.getCard(parentKey);
|
|
239
|
+
if (parentCard) {
|
|
240
|
+
// Add or update the child in the parent's children array
|
|
241
|
+
const existingChildIndex = parentCard.children?.findIndex(
|
|
242
|
+
(child) => child === newChild.key,
|
|
243
|
+
);
|
|
244
|
+
if (existingChildIndex === -1) {
|
|
245
|
+
parentCard.children.push(newChild.key);
|
|
246
|
+
}
|
|
247
|
+
this.cardCache.updateCard(parentCard.key, parentCard);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Populate template cards into the card cache.
|
|
253
|
+
*/
|
|
254
|
+
protected async populateTemplateCards(): Promise<void> {
|
|
255
|
+
try {
|
|
256
|
+
// Gets local & module templates
|
|
257
|
+
const templateResources = await this.templates();
|
|
258
|
+
const prefixes = await this.projectPrefixes();
|
|
259
|
+
const loadPromises = templateResources.map(async (template) => {
|
|
260
|
+
try {
|
|
261
|
+
this.validator.validResourceName(
|
|
262
|
+
'templates',
|
|
263
|
+
template.name,
|
|
264
|
+
prefixes,
|
|
265
|
+
);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.logger.warn(
|
|
268
|
+
{ templateName: template.name, error },
|
|
269
|
+
`Template name '${template.name}' does not follow required format, skipping`,
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const templateResource = new TemplateResource(
|
|
275
|
+
this,
|
|
276
|
+
resourceName(template.name),
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const templateObject = templateResource.templateObject();
|
|
280
|
+
const isCreated = templateObject && templateObject.isCreated();
|
|
281
|
+
if (!templateObject || !isCreated) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
await this.cardCache.populateFromPath(
|
|
286
|
+
templateObject.templateCardsFolder(),
|
|
287
|
+
false,
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
await Promise.all(loadPromises);
|
|
292
|
+
|
|
293
|
+
// Once all templates have been fetched, build child-parent relationships.
|
|
294
|
+
this.cardCache.populateChildrenRelationships();
|
|
295
|
+
} catch (error) {
|
|
296
|
+
this.logger.error(
|
|
297
|
+
{ error },
|
|
298
|
+
'Failed to populate template cards into the card cache',
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Populate both the project cards, and all template cards into card cache.
|
|
305
|
+
*/
|
|
306
|
+
protected async populateCardsCache(): Promise<void> {
|
|
307
|
+
await this.cardCache.populateFromPath(this.paths.cardRootFolder);
|
|
308
|
+
await this.populateTemplateCards();
|
|
309
|
+
}
|
|
310
|
+
|
|
166
311
|
/**
|
|
167
312
|
* Add a given 'resource' to the local resource arrays.
|
|
168
313
|
* @param resource Resource to add.
|
|
@@ -173,14 +318,52 @@ export class Project extends CardContainer {
|
|
|
173
318
|
this.createdResources.set(resource.name, data);
|
|
174
319
|
}
|
|
175
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Returns all template cards from the project. This includes all module templates' cards.
|
|
323
|
+
* @returns all the template cards from the project
|
|
324
|
+
*/
|
|
325
|
+
public allTemplateCards(): Card[] {
|
|
326
|
+
return this.cardCache.getAllTemplateCards();
|
|
327
|
+
}
|
|
328
|
+
|
|
176
329
|
/**
|
|
177
330
|
* Returns an array of all the attachments in the project card's (excluding ones in templates).
|
|
178
331
|
* @returns all attachments in the project.
|
|
179
332
|
*/
|
|
180
|
-
public
|
|
333
|
+
public attachments(): CardAttachment[] {
|
|
181
334
|
return super.attachments(this.paths.cardRootFolder);
|
|
182
335
|
}
|
|
183
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Returns attachments from cards at a specific path using the card cache.
|
|
339
|
+
* This method allows templates to access attachments from the shared cache.
|
|
340
|
+
* @param path The path to get attachments from
|
|
341
|
+
* @returns Array of attachments from cards at the specified path
|
|
342
|
+
*/
|
|
343
|
+
public attachmentsByPath(path: string): CardAttachment[] {
|
|
344
|
+
return super.attachments(path);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Returns all the attachments in the template cards.
|
|
349
|
+
* @returns all the attachments in the template cards.
|
|
350
|
+
*/
|
|
351
|
+
public async attachmentsFromTemplates() {
|
|
352
|
+
const templateAttachments: CardAttachment[] = [];
|
|
353
|
+
const templates = await this.templates();
|
|
354
|
+
for (const template of templates) {
|
|
355
|
+
const templateResource = new TemplateResource(
|
|
356
|
+
this,
|
|
357
|
+
resourceName(template.name),
|
|
358
|
+
);
|
|
359
|
+
const templateObject = templateResource.templateObject();
|
|
360
|
+
if (templateObject) {
|
|
361
|
+
templateAttachments.push(...templateObject.attachments());
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return templateAttachments;
|
|
365
|
+
}
|
|
366
|
+
|
|
184
367
|
/**
|
|
185
368
|
* Returns an array of all the calculation files (*.lp) in the project.
|
|
186
369
|
* @param from Defines where resources are collected from.
|
|
@@ -193,59 +376,111 @@ export class Project extends CardContainer {
|
|
|
193
376
|
}
|
|
194
377
|
|
|
195
378
|
/**
|
|
196
|
-
* Returns path to card's attachment folder.
|
|
379
|
+
* Returns path to a card's attachment folder.
|
|
197
380
|
* @param cardKey card key
|
|
198
|
-
* @returns path to card's attachment folder.
|
|
199
|
-
|
|
381
|
+
* @returns path to a card's attachment folder.
|
|
382
|
+
*/
|
|
383
|
+
public cardAttachmentFolder(cardKey: string): string {
|
|
384
|
+
const pathToCard = this.findCard(cardKey).path;
|
|
385
|
+
return join(pathToCard, 'a');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Creates an attachment for a card.
|
|
390
|
+
* @param cardKey The card to add attachment to
|
|
391
|
+
* @param attachmentName The name for the attachment file
|
|
392
|
+
* @param attachmentData The attachment data (file path or buffer)
|
|
393
|
+
* @throws If trying to add attachment to module card, or if attachment is not found
|
|
200
394
|
*/
|
|
201
|
-
public async
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
395
|
+
public async createCardAttachment(
|
|
396
|
+
cardKey: string,
|
|
397
|
+
attachmentName: string,
|
|
398
|
+
attachmentData: string | Buffer,
|
|
399
|
+
): Promise<void> {
|
|
400
|
+
const attachmentFolder = this.cardAttachmentFolder(cardKey);
|
|
401
|
+
|
|
402
|
+
// Check if this is a module template
|
|
403
|
+
if (isModulePath(attachmentFolder)) {
|
|
404
|
+
throw new Error(`Cannot modify imported module`);
|
|
206
405
|
}
|
|
207
406
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
407
|
+
// Create the attachment folder if it doesn't exist
|
|
408
|
+
await mkdir(attachmentFolder, { recursive: true });
|
|
409
|
+
|
|
410
|
+
const attachmentPath = join(attachmentFolder, basename(attachmentName));
|
|
411
|
+
|
|
412
|
+
if (Buffer.isBuffer(attachmentData)) {
|
|
413
|
+
await writeFile(attachmentPath, attachmentData, { flag: 'wx' });
|
|
414
|
+
} else {
|
|
415
|
+
try {
|
|
416
|
+
await copyFile(
|
|
417
|
+
attachmentData,
|
|
418
|
+
attachmentPath,
|
|
419
|
+
fsConstants.COPYFILE_EXCL,
|
|
420
|
+
);
|
|
421
|
+
} catch {
|
|
422
|
+
throw new Error(`Attachment file not found: ${attachmentData}`);
|
|
423
|
+
}
|
|
211
424
|
}
|
|
212
|
-
|
|
425
|
+
|
|
426
|
+
// Update cache
|
|
427
|
+
await this.handleAttachmentChange(
|
|
428
|
+
cardKey,
|
|
429
|
+
'added',
|
|
430
|
+
basename(attachmentName),
|
|
431
|
+
);
|
|
213
432
|
}
|
|
214
433
|
|
|
215
434
|
/**
|
|
216
|
-
*
|
|
217
|
-
* @param cardKey card
|
|
218
|
-
* @param
|
|
219
|
-
* @
|
|
435
|
+
* Removes an attachment from a card.
|
|
436
|
+
* @param cardKey The card to remove attachment from
|
|
437
|
+
* @param fileName The name of the attachment file to remove
|
|
438
|
+
* @throws if trying to remove module card attachment, or the attachment was not found.
|
|
220
439
|
*/
|
|
221
|
-
public async
|
|
440
|
+
public async removeCardAttachment(
|
|
222
441
|
cardKey: string,
|
|
223
|
-
|
|
224
|
-
): Promise<
|
|
225
|
-
|
|
442
|
+
fileName: string,
|
|
443
|
+
): Promise<void> {
|
|
444
|
+
const attachmentFolder = this.cardAttachmentFolder(cardKey);
|
|
445
|
+
|
|
446
|
+
// Modules cannot be modified.
|
|
447
|
+
if (isModulePath(attachmentFolder)) {
|
|
448
|
+
throw new Error(`Cannot modify imported module`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const attachmentPath = join(attachmentFolder, fileName);
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
await unlink(attachmentPath);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
this.logger.error({ error }, 'Removing card attachment');
|
|
457
|
+
throw new Error(`Attachment not found: ${fileName}`);
|
|
458
|
+
}
|
|
459
|
+
await this.handleAttachmentChange(cardKey, 'removed', fileName);
|
|
226
460
|
}
|
|
227
461
|
|
|
228
462
|
/**
|
|
229
|
-
* Returns path to card's folder.
|
|
463
|
+
* Returns path to a card's folder.
|
|
230
464
|
* @param cardKey card key
|
|
231
|
-
* @returns path to card's folder.
|
|
465
|
+
* @returns path to a card's folder.
|
|
232
466
|
*/
|
|
233
467
|
public async cardFolder(cardKey: string): Promise<string> {
|
|
234
|
-
const found =
|
|
468
|
+
const found = super.findCard(cardKey);
|
|
235
469
|
if (found) {
|
|
236
470
|
return found.path;
|
|
237
471
|
}
|
|
238
472
|
|
|
239
473
|
const templates = await this.templates();
|
|
240
|
-
const templatePromises = templates.map(
|
|
474
|
+
const templatePromises = templates.map((template) => {
|
|
241
475
|
const templateObject = new TemplateResource(
|
|
242
476
|
this,
|
|
243
477
|
resourceName(template.name),
|
|
244
478
|
).templateObject();
|
|
245
479
|
const templateCard = templateObject
|
|
246
|
-
?
|
|
480
|
+
? templateObject.findCard(cardKey)
|
|
247
481
|
: undefined;
|
|
248
|
-
|
|
482
|
+
const path = templateCard ? templateCard.path : '';
|
|
483
|
+
return path;
|
|
249
484
|
});
|
|
250
485
|
|
|
251
486
|
const templatePaths = await Promise.all(templatePromises);
|
|
@@ -253,78 +488,41 @@ export class Project extends CardContainer {
|
|
|
253
488
|
}
|
|
254
489
|
|
|
255
490
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* @
|
|
259
|
-
* @returns card path logical parts
|
|
260
|
-
* @throws when called with wrong path, or wrong card owner
|
|
261
|
-
* todo: if prefix would be parameter; this could be static, or util method
|
|
491
|
+
* Fetches full Card data for given card keys
|
|
492
|
+
* @param cardIds array of card keys to fetch
|
|
493
|
+
* @returns Card data to the given card keys
|
|
262
494
|
*/
|
|
263
|
-
public
|
|
264
|
-
const
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
let startIndex = -1;
|
|
270
|
-
let templatesNameIndex = -1;
|
|
271
|
-
|
|
272
|
-
const cardRootIndex = pathParts.indexOf('cardRoot');
|
|
273
|
-
const projectInternalsIndex = pathParts.indexOf('.cards');
|
|
274
|
-
|
|
275
|
-
if (projectInternalsIndex === -1 && cardRootIndex >= 0) {
|
|
276
|
-
startIndex = projectInternalsIndex;
|
|
277
|
-
} else if (projectInternalsIndex >= 0 && cardRootIndex === -1) {
|
|
278
|
-
const templatesIndex = pathParts.indexOf('templates');
|
|
279
|
-
startIndex = templatesIndex;
|
|
280
|
-
if (templatesIndex === -1) {
|
|
281
|
-
throw new Error(
|
|
282
|
-
`Invalid card path. Template card must have 'templates' in path`,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
const modulesIndex = pathParts.indexOf('modules');
|
|
286
|
-
if (modulesIndex !== -1) {
|
|
287
|
-
prefix = pathParts.at(modulesIndex + 1) || '';
|
|
288
|
-
}
|
|
289
|
-
templatesNameIndex = templatesIndex + 1;
|
|
290
|
-
template = `${prefix}/templates/${pathParts.at(templatesNameIndex)}`;
|
|
291
|
-
} else {
|
|
292
|
-
throw new Error(`Card must be either project card, or template card`);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Look for parents in the path.
|
|
296
|
-
let previousWasParent = false;
|
|
297
|
-
for (let index = startIndex; index <= pathParts.length; index++) {
|
|
298
|
-
if (previousWasParent) {
|
|
299
|
-
previousWasParent = false;
|
|
300
|
-
parents.push(pathParts.at(index - 2));
|
|
301
|
-
}
|
|
302
|
-
const cardsSubFolder = pathParts.at(index) === 'c';
|
|
303
|
-
const ignoreOrNotTemplatesParent =
|
|
304
|
-
index - 1 !== templatesNameIndex || templatesNameIndex === -1;
|
|
305
|
-
if (cardsSubFolder && ignoreOrNotTemplatesParent) {
|
|
306
|
-
previousWasParent = true;
|
|
495
|
+
public cardKeysToCards(cardIds: string[]): Card[] {
|
|
496
|
+
const cards: Card[] = [];
|
|
497
|
+
for (const cardId of cardIds) {
|
|
498
|
+
const card = this.cardCache.getCard(cardId);
|
|
499
|
+
if (card) {
|
|
500
|
+
cards.push(card);
|
|
307
501
|
}
|
|
308
502
|
}
|
|
503
|
+
return cards;
|
|
504
|
+
}
|
|
309
505
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Accessor for cards cache.
|
|
508
|
+
* Used by template container (it needs to access project's cache, not their own instance).
|
|
509
|
+
* @note Should not be used directly (other than Template).
|
|
510
|
+
*/
|
|
511
|
+
public get cardsCache() {
|
|
512
|
+
return this.cardCache;
|
|
316
513
|
}
|
|
317
514
|
|
|
318
515
|
/**
|
|
319
|
-
* Returns an array of all the cards in the project.
|
|
320
|
-
* @
|
|
321
|
-
* @param
|
|
516
|
+
* Returns an array of all the cards in the project.
|
|
517
|
+
* @note These are project cards only, by default (unless path dictates otherwise).
|
|
518
|
+
* @param path Path from which to fetch the cards. Generally it is best to fetch from Project root, e.g. Project.cardRootFolder
|
|
519
|
+
* @param details Which details to include in the cards; by default all details are included.
|
|
322
520
|
* @returns all cards from the given path in the project.
|
|
323
521
|
*/
|
|
324
|
-
public
|
|
522
|
+
public cards(
|
|
325
523
|
path: string = this.paths.cardRootFolder,
|
|
326
|
-
details
|
|
327
|
-
):
|
|
524
|
+
details?: FetchCardDetails,
|
|
525
|
+
): Card[] {
|
|
328
526
|
return super.cards(path, details);
|
|
329
527
|
}
|
|
330
528
|
|
|
@@ -339,6 +537,22 @@ export class Project extends CardContainer {
|
|
|
339
537
|
return this.resources.resources('cardTypes', from);
|
|
340
538
|
}
|
|
341
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Returns children of a given card; as Card array
|
|
542
|
+
* @param card Parent card to fetch children from
|
|
543
|
+
* @returns children of a given card; as Card array
|
|
544
|
+
*/
|
|
545
|
+
public childrenCards(card: Card): Card[] {
|
|
546
|
+
const cards: Card[] = [];
|
|
547
|
+
for (const child of card.children) {
|
|
548
|
+
const card = this.cardCache.getCard(child);
|
|
549
|
+
if (card) {
|
|
550
|
+
cards.push(card);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return cards;
|
|
554
|
+
}
|
|
555
|
+
|
|
342
556
|
/**
|
|
343
557
|
* Updates all local resources.
|
|
344
558
|
*/
|
|
@@ -349,8 +563,8 @@ export class Project extends CardContainer {
|
|
|
349
563
|
/**
|
|
350
564
|
* Updates all imported module resources.
|
|
351
565
|
*/
|
|
352
|
-
public
|
|
353
|
-
|
|
566
|
+
public collectModuleResources() {
|
|
567
|
+
this.resources.moduleImported();
|
|
354
568
|
}
|
|
355
569
|
|
|
356
570
|
/**
|
|
@@ -370,7 +584,7 @@ export class Project extends CardContainer {
|
|
|
370
584
|
if (!card || !card.path || !isTemplateCard(card)) {
|
|
371
585
|
return undefined;
|
|
372
586
|
}
|
|
373
|
-
const { template } = this.
|
|
587
|
+
const { template } = cardPathParts(this.projectPrefix, card.path);
|
|
374
588
|
return new TemplateResource(this, resourceName(template)).templateObject();
|
|
375
589
|
}
|
|
376
590
|
|
|
@@ -420,46 +634,8 @@ export class Project extends CardContainer {
|
|
|
420
634
|
* @param details Defines which card details are included in the return values.
|
|
421
635
|
* @returns specific card details, or undefined if card is not part of the project.
|
|
422
636
|
*/
|
|
423
|
-
public
|
|
424
|
-
cardToFind
|
|
425
|
-
details: ProjectFetchCardDetails = {},
|
|
426
|
-
): Promise<Card | undefined> {
|
|
427
|
-
let card;
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
details.location === CardLocation.projectOnly ||
|
|
431
|
-
details.location === CardLocation.all ||
|
|
432
|
-
!details.location
|
|
433
|
-
) {
|
|
434
|
-
card = await super.findCard(
|
|
435
|
-
this.paths.cardRootFolder,
|
|
436
|
-
cardToFind,
|
|
437
|
-
details,
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (
|
|
442
|
-
!card &&
|
|
443
|
-
(details.location === CardLocation.templatesOnly ||
|
|
444
|
-
details.location === CardLocation.all ||
|
|
445
|
-
!details.location)
|
|
446
|
-
) {
|
|
447
|
-
const templates = await this.templates();
|
|
448
|
-
for (const template of templates) {
|
|
449
|
-
const templateObject = new TemplateResource(
|
|
450
|
-
this,
|
|
451
|
-
resourceName(template.name),
|
|
452
|
-
).templateObject();
|
|
453
|
-
if (!templateObject) continue;
|
|
454
|
-
|
|
455
|
-
// optimize: execute each find in template parallel
|
|
456
|
-
card = await templateObject.findSpecificCard(cardToFind, details);
|
|
457
|
-
if (card) {
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return card;
|
|
637
|
+
public findCard(cardToFind: string, details?: ProjectFetchCardDetails): Card {
|
|
638
|
+
return super.findCard(cardToFind, details);
|
|
463
639
|
}
|
|
464
640
|
|
|
465
641
|
/**
|
|
@@ -489,6 +665,7 @@ export class Project extends CardContainer {
|
|
|
489
665
|
* @param changedCard Card that was changed.
|
|
490
666
|
*/
|
|
491
667
|
public async handleCardChanged(changedCard: Card) {
|
|
668
|
+
// Notify the calculation engine about the change
|
|
492
669
|
return this.calculationEngine.handleCardChanged(changedCard);
|
|
493
670
|
}
|
|
494
671
|
|
|
@@ -496,39 +673,86 @@ export class Project extends CardContainer {
|
|
|
496
673
|
* When cards are removed.
|
|
497
674
|
* @param deletedCard Card that is to be removed.
|
|
498
675
|
*/
|
|
499
|
-
public async
|
|
676
|
+
public async handleCardDeleted(deletedCard: Card) {
|
|
677
|
+
// Delete children from the cache first
|
|
678
|
+
if (deletedCard.children && deletedCard.children.length > 0) {
|
|
679
|
+
for (const child of deletedCard.children) {
|
|
680
|
+
try {
|
|
681
|
+
const childCard = this.findCard(child);
|
|
682
|
+
await this.handleCardDeleted(childCard);
|
|
683
|
+
} catch {
|
|
684
|
+
this.logger.warn(
|
|
685
|
+
`Accessing child '${child}' of '${deletedCard.key}' when deleting cards caused an exception`,
|
|
686
|
+
);
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
await super.removeCard(deletedCard.key);
|
|
500
692
|
return this.calculationEngine.handleDeleteCard(deletedCard);
|
|
501
693
|
}
|
|
502
694
|
|
|
503
695
|
/**
|
|
504
|
-
* When
|
|
505
|
-
* @param
|
|
696
|
+
* When card is moved.
|
|
697
|
+
* @param movedCard Card that moved
|
|
698
|
+
* @param newParentCard New parent for the 'movedCard'
|
|
699
|
+
* @param oldParentCard Previous parent of the 'movedCard'
|
|
506
700
|
*/
|
|
507
|
-
public async
|
|
508
|
-
|
|
701
|
+
public async handleCardMoved(
|
|
702
|
+
movedCard: Card,
|
|
703
|
+
newParentCard?: Card,
|
|
704
|
+
oldParentCard?: Card,
|
|
705
|
+
) {
|
|
706
|
+
if (newParentCard) {
|
|
707
|
+
this.cardCache.updateCard(newParentCard.key, newParentCard);
|
|
708
|
+
}
|
|
709
|
+
if (oldParentCard) {
|
|
710
|
+
this.cardCache.updateCard(oldParentCard.key, oldParentCard);
|
|
711
|
+
}
|
|
712
|
+
this.cardCache.updateCard(movedCard.key, movedCard);
|
|
713
|
+
|
|
714
|
+
// todo: it would be enough to just update parent, previous parent and changed card
|
|
715
|
+
this.cardCache.populateChildrenRelationships();
|
|
716
|
+
await this.handleCardChanged(movedCard);
|
|
717
|
+
await this.calculationEngine.handleCardMoved();
|
|
509
718
|
}
|
|
510
719
|
|
|
511
720
|
/**
|
|
512
|
-
*
|
|
513
|
-
* @param
|
|
514
|
-
* @returns true if a given card is found from project, false otherwise.
|
|
721
|
+
* When new cards are added.
|
|
722
|
+
* @param cards Added cards.
|
|
515
723
|
*/
|
|
516
|
-
public
|
|
517
|
-
|
|
724
|
+
public async handleNewCards(cards: Card[]) {
|
|
725
|
+
// Add new cards to the card cache
|
|
726
|
+
cards.forEach((card) => {
|
|
727
|
+
const cardWithParent = {
|
|
728
|
+
...card,
|
|
729
|
+
parent: card.parent || this.parentFromPath(card.path),
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
this.cardCache.updateCard(cardWithParent.key, cardWithParent);
|
|
733
|
+
|
|
734
|
+
// Update the parent's children list in the cache
|
|
735
|
+
if (cardWithParent.parent && cardWithParent.parent !== ROOT) {
|
|
736
|
+
this.updateCachedChildren(cardWithParent.parent, cardWithParent);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
return this.calculationEngine.handleNewCards(cards);
|
|
518
740
|
}
|
|
519
741
|
|
|
520
742
|
/**
|
|
521
743
|
* Adds a module from project.
|
|
522
|
-
* @param module
|
|
744
|
+
* @param module Module to add
|
|
523
745
|
*/
|
|
524
746
|
public async importModule(module: ModuleSetting) {
|
|
525
747
|
// Add module as a dependency.
|
|
526
748
|
await this.configuration.addModule(module);
|
|
527
|
-
|
|
749
|
+
this.collectModuleResources();
|
|
750
|
+
await this.populateTemplateCards();
|
|
751
|
+
this.logger.info(`Imported module '${module.name}'`);
|
|
528
752
|
}
|
|
529
753
|
|
|
530
754
|
/**
|
|
531
|
-
* Checks if given path is a project.
|
|
755
|
+
* Checks if a given path is a project.
|
|
532
756
|
* @param path Path to a project
|
|
533
757
|
* @returns true, if in the given path there is a project; false otherwise
|
|
534
758
|
*/
|
|
@@ -536,17 +760,6 @@ export class Project extends CardContainer {
|
|
|
536
760
|
return pathExists(join(path, 'cardRoot'));
|
|
537
761
|
}
|
|
538
762
|
|
|
539
|
-
/**
|
|
540
|
-
* Returns whether card is a template card or not
|
|
541
|
-
* @param cardKey card to check.
|
|
542
|
-
* @todo: This is only used from 'remove'. Could it use the static checker?
|
|
543
|
-
* @returns true, if card is template card; false otherwise
|
|
544
|
-
*/
|
|
545
|
-
public async isTemplateCard(cardKey: string): Promise<boolean> {
|
|
546
|
-
const templateCards = await this.allTemplateCards();
|
|
547
|
-
return templateCards.find((card) => card.key === cardKey) != null;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
763
|
/**
|
|
551
764
|
* Returns an array of all the link types in the project.
|
|
552
765
|
* @param from Defines where resources are collected from.
|
|
@@ -562,7 +775,7 @@ export class Project extends CardContainer {
|
|
|
562
775
|
* Returns an array of cards in the project, in the templates or both.
|
|
563
776
|
* Cards don't have content and nor metadata.
|
|
564
777
|
* @param cardsFrom Where to return cards from (project, templates, or both)
|
|
565
|
-
* @returns all cards in the project.
|
|
778
|
+
* @returns all cards in the project per container.
|
|
566
779
|
*/
|
|
567
780
|
public async listCards(
|
|
568
781
|
cardsFrom: CardLocation = CardLocation.all,
|
|
@@ -572,9 +785,9 @@ export class Project extends CardContainer {
|
|
|
572
785
|
cardsFrom === CardLocation.all ||
|
|
573
786
|
cardsFrom === CardLocation.projectOnly
|
|
574
787
|
) {
|
|
575
|
-
const projectCards =
|
|
576
|
-
(
|
|
577
|
-
|
|
788
|
+
const projectCards = super
|
|
789
|
+
.cards(this.paths.cardRootFolder)
|
|
790
|
+
.map((item) => item.key);
|
|
578
791
|
cardListContainer.push({
|
|
579
792
|
name: this.projectName,
|
|
580
793
|
type: 'project',
|
|
@@ -594,8 +807,8 @@ export class Project extends CardContainer {
|
|
|
594
807
|
).templateObject();
|
|
595
808
|
if (templateObject) {
|
|
596
809
|
// todo: optimization - do all this in parallel
|
|
597
|
-
const templateCards =
|
|
598
|
-
if (templateCards) {
|
|
810
|
+
const templateCards = templateObject.listCards();
|
|
811
|
+
if (templateCards.length) {
|
|
599
812
|
cardListContainer.push({
|
|
600
813
|
name: template.name,
|
|
601
814
|
type: 'template',
|
|
@@ -617,69 +830,27 @@ export class Project extends CardContainer {
|
|
|
617
830
|
public async listCardIds(
|
|
618
831
|
cardsFrom: CardLocation = CardLocation.all,
|
|
619
832
|
): Promise<Set<string>> {
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
promises.push(
|
|
626
|
-
super
|
|
627
|
-
.cards(this.paths.cardRootFolder)
|
|
628
|
-
.then((cards) => new Set(cards.map((card) => card.key))),
|
|
629
|
-
);
|
|
833
|
+
const cardContainers = await this.listCards(cardsFrom);
|
|
834
|
+
const allCardIDs = new Set<string>();
|
|
835
|
+
for (const container of cardContainers) {
|
|
836
|
+
const cards = container.cards;
|
|
837
|
+
cards.forEach((card) => allCardIDs.add(card));
|
|
630
838
|
}
|
|
631
|
-
|
|
632
|
-
cardsFrom === CardLocation.all ||
|
|
633
|
-
cardsFrom === CardLocation.templatesOnly
|
|
634
|
-
) {
|
|
635
|
-
promises.push(
|
|
636
|
-
(async () => {
|
|
637
|
-
const templates = await this.templates();
|
|
638
|
-
const templateResources = templates.map(
|
|
639
|
-
(template) =>
|
|
640
|
-
new TemplateResource(this, resourceName(template.name)),
|
|
641
|
-
);
|
|
642
|
-
const templateObjectsResults =
|
|
643
|
-
await Promise.allSettled(templateResources);
|
|
644
|
-
const templateObjects = templateObjectsResults
|
|
645
|
-
.filter(
|
|
646
|
-
(result): result is PromiseFulfilledResult<TemplateResource> =>
|
|
647
|
-
result.status === 'fulfilled' && result.value !== null,
|
|
648
|
-
)
|
|
649
|
-
.map((result) => result.value);
|
|
650
|
-
|
|
651
|
-
const listCardsResults = await Promise.allSettled(
|
|
652
|
-
templateObjects.map((obj) => obj.templateObject().listCards()),
|
|
653
|
-
);
|
|
654
|
-
const templateCardIds = new Set<string>();
|
|
655
|
-
listCardsResults
|
|
656
|
-
.filter(
|
|
657
|
-
(result): result is PromiseFulfilledResult<Card[]> =>
|
|
658
|
-
result.status === 'fulfilled',
|
|
659
|
-
)
|
|
660
|
-
.forEach((result) => {
|
|
661
|
-
result.value.forEach((card) => templateCardIds.add(card.key));
|
|
662
|
-
});
|
|
663
|
-
return templateCardIds;
|
|
664
|
-
})(),
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
const allCardIdSets = await Promise.all(promises);
|
|
668
|
-
return new Set(allCardIdSets.flatMap((set) => [...set]));
|
|
839
|
+
return allCardIDs;
|
|
669
840
|
}
|
|
670
841
|
|
|
671
842
|
/**
|
|
672
843
|
* Returns details of a certain module.
|
|
673
844
|
* @param moduleName Name of the module.
|
|
674
|
-
* @returns module details, or undefined if
|
|
845
|
+
* @returns module details, or undefined if module cannot be found.
|
|
675
846
|
*/
|
|
676
847
|
public async module(moduleName: string): Promise<ModuleContent | undefined> {
|
|
677
848
|
const module = await this.findModule(moduleName);
|
|
678
849
|
if (module && module.path) {
|
|
679
850
|
const modulePath = join(module.path, module.name);
|
|
680
|
-
const moduleConfig =
|
|
851
|
+
const moduleConfig = await readJsonFile(
|
|
681
852
|
join(modulePath, Project.projectConfigFileName),
|
|
682
|
-
)
|
|
853
|
+
);
|
|
683
854
|
return {
|
|
684
855
|
name: moduleConfig.name,
|
|
685
856
|
modules: moduleConfig.modules,
|
|
@@ -821,15 +992,12 @@ export class Project extends CardContainer {
|
|
|
821
992
|
}
|
|
822
993
|
|
|
823
994
|
/**
|
|
824
|
-
*
|
|
825
|
-
* @param cardKey card to check path for.
|
|
826
|
-
* @returns path to a given card.
|
|
995
|
+
* Populates the card cache, if it has not been populated.
|
|
827
996
|
*/
|
|
828
|
-
public
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
return foundFile ? dirname(foundFile) : '';
|
|
997
|
+
public async populateCaches() {
|
|
998
|
+
if (!this.cardCache.isPopulated) {
|
|
999
|
+
await this.populateCardsCache();
|
|
1000
|
+
}
|
|
833
1001
|
}
|
|
834
1002
|
|
|
835
1003
|
/**
|
|
@@ -856,6 +1024,7 @@ export class Project extends CardContainer {
|
|
|
856
1024
|
const prefixes: string[] = [this.projectPrefix];
|
|
857
1025
|
let files;
|
|
858
1026
|
try {
|
|
1027
|
+
// TODO: Could be optimized so that prefixes are stored once fetched.
|
|
859
1028
|
files = await readdir(this.paths.modulesFolder, {
|
|
860
1029
|
withFileTypes: true,
|
|
861
1030
|
recursive: true,
|
|
@@ -873,13 +1042,48 @@ export class Project extends CardContainer {
|
|
|
873
1042
|
|
|
874
1043
|
const configurationPrefixes = await Promise.all(configurationPromises);
|
|
875
1044
|
prefixes.push(...configurationPrefixes);
|
|
876
|
-
} catch {
|
|
877
|
-
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
this.logger.error({ error }, 'Failed to collect prefixes in use');
|
|
878
1047
|
}
|
|
879
1048
|
|
|
880
1049
|
return prefixes;
|
|
881
1050
|
}
|
|
882
1051
|
|
|
1052
|
+
/**
|
|
1053
|
+
* Removes a module from the project
|
|
1054
|
+
* @param module Module (name) to remove.
|
|
1055
|
+
*/
|
|
1056
|
+
public async removeModule(moduleName: string) {
|
|
1057
|
+
const toBeRemovedTemplates = this.resources.moduleResources.resourceArray(
|
|
1058
|
+
'templates',
|
|
1059
|
+
moduleName,
|
|
1060
|
+
);
|
|
1061
|
+
// First, remove cards from the cache
|
|
1062
|
+
for (const template of toBeRemovedTemplates) {
|
|
1063
|
+
this.cardCache.deleteCardsFromTemplate(template.name);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Then, remove module from project configuration
|
|
1067
|
+
await this.configuration.removeModule(moduleName);
|
|
1068
|
+
this.collectModuleResources();
|
|
1069
|
+
|
|
1070
|
+
this.logger.info(`Removed module '${moduleName}'`);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Removes a resource from Project.
|
|
1075
|
+
* @param resource Resource to remove.
|
|
1076
|
+
*/
|
|
1077
|
+
public removeResource(resource: Resource) {
|
|
1078
|
+
// Template cards must be removed from the cache when resource is removed.
|
|
1079
|
+
if (resource.path.includes('templates')) {
|
|
1080
|
+
const templateName = resourceNameToString(resourceName(resource.name));
|
|
1081
|
+
this.cardCache.deleteCardsFromTemplate(templateName);
|
|
1082
|
+
}
|
|
1083
|
+
this.resources.remove(resource);
|
|
1084
|
+
this.createdResources.delete(resource.name);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
883
1087
|
/**
|
|
884
1088
|
* Array of reports in the project.
|
|
885
1089
|
* @param from Defines where resources are collected from.
|
|
@@ -907,21 +1111,12 @@ export class Project extends CardContainer {
|
|
|
907
1111
|
return handleBarFiles;
|
|
908
1112
|
}
|
|
909
1113
|
|
|
910
|
-
/**
|
|
911
|
-
* Removes a resource from Project.
|
|
912
|
-
* @param resource Resource to remove.
|
|
913
|
-
*/
|
|
914
|
-
public removeResource(resource: Resource) {
|
|
915
|
-
this.resources.remove(resource);
|
|
916
|
-
this.createdResources.delete(resource.name);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
1114
|
/**
|
|
920
1115
|
* Returns metadata from a given resource
|
|
921
1116
|
* @param name Name of a resource
|
|
922
1117
|
* @returns Metadata from the resource.
|
|
923
1118
|
*/
|
|
924
|
-
public
|
|
1119
|
+
public resource<Type>(name: string): Type | undefined {
|
|
925
1120
|
const resName = resourceName(name);
|
|
926
1121
|
if (this.createdResources.has(resourceNameToString(resName))) {
|
|
927
1122
|
const value = this.createdResources.get(
|
|
@@ -977,7 +1172,9 @@ export class Project extends CardContainer {
|
|
|
977
1172
|
* @returns Created resource.
|
|
978
1173
|
*/
|
|
979
1174
|
public static resourceObject(project: Project, name: ResourceName) {
|
|
980
|
-
if (name.type === '
|
|
1175
|
+
if (name.type === 'calculations') {
|
|
1176
|
+
return new CalculationResource(project, name);
|
|
1177
|
+
} else if (name.type === 'cardTypes') {
|
|
981
1178
|
return new CardTypeResource(project, name);
|
|
982
1179
|
} else if (name.type === 'fieldTypes') {
|
|
983
1180
|
return new FieldTypeResource(project, name);
|
|
@@ -1019,45 +1216,23 @@ export class Project extends CardContainer {
|
|
|
1019
1216
|
* Show cards of a project.
|
|
1020
1217
|
* @returns an array of all project cards in the project.
|
|
1021
1218
|
*/
|
|
1022
|
-
public
|
|
1219
|
+
public showProjectCards(): Card[] {
|
|
1023
1220
|
return this.showCards(this.paths.cardRootFolder);
|
|
1024
1221
|
}
|
|
1025
1222
|
|
|
1026
|
-
/**
|
|
1027
|
-
* Returns all template cards from the project. This includes all module templates' cards.
|
|
1028
|
-
* @param cardDetails which details to fetch. Optional.
|
|
1029
|
-
* @returns all the template cards from the project
|
|
1030
|
-
*/
|
|
1031
|
-
public async allTemplateCards(
|
|
1032
|
-
cardDetails?: FetchCardDetails,
|
|
1033
|
-
): Promise<Card[]> {
|
|
1034
|
-
const templates = await this.templates();
|
|
1035
|
-
const cards: Card[] = [];
|
|
1036
|
-
for (const template of templates) {
|
|
1037
|
-
const templateCards = await this.templateCards(
|
|
1038
|
-
template.name,
|
|
1039
|
-
cardDetails,
|
|
1040
|
-
);
|
|
1041
|
-
if (templateCards) cards.push(...templateCards);
|
|
1042
|
-
}
|
|
1043
|
-
return cards;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
1223
|
/**
|
|
1047
1224
|
* Returns cards from single template.
|
|
1048
|
-
* @param templateName Name of the template
|
|
1049
|
-
* @param cardDetails Card information
|
|
1225
|
+
* @param templateName Name of the template (supports both full names like 'decision/templates/decision' and short names like 'decision')
|
|
1050
1226
|
* @returns List of cards from template.
|
|
1051
1227
|
*/
|
|
1052
|
-
public
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
)
|
|
1060
|
-
return await templateObject?.cards('', cardDetails);
|
|
1228
|
+
public templateCards(templateName: string): Card[] {
|
|
1229
|
+
const templateCards = this.cardCache.getAllTemplateCards();
|
|
1230
|
+
return templateCards.filter((cachedCard) => {
|
|
1231
|
+
if (cachedCard.location === 'project') {
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
return cachedCard.location === templateName;
|
|
1235
|
+
});
|
|
1061
1236
|
}
|
|
1062
1237
|
|
|
1063
1238
|
/**
|
|
@@ -1072,20 +1247,21 @@ export class Project extends CardContainer {
|
|
|
1072
1247
|
}
|
|
1073
1248
|
|
|
1074
1249
|
/**
|
|
1075
|
-
* Update card content.
|
|
1076
|
-
* @param cardKey card
|
|
1250
|
+
* Update a card's content.
|
|
1251
|
+
* @param cardKey card key to update.
|
|
1077
1252
|
* @param content changed content
|
|
1078
1253
|
*/
|
|
1079
1254
|
public async updateCardContent(cardKey: string, content: string) {
|
|
1080
|
-
const card =
|
|
1081
|
-
metadata: true,
|
|
1082
|
-
content: true,
|
|
1083
|
-
});
|
|
1084
|
-
if (!card) {
|
|
1085
|
-
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
1086
|
-
}
|
|
1255
|
+
const card = this.findCard(cardKey);
|
|
1087
1256
|
card.content = content;
|
|
1257
|
+
|
|
1258
|
+
// Update lastUpdated timestamp in metadata
|
|
1259
|
+
if (card.metadata) {
|
|
1260
|
+
card.metadata.lastUpdated = new Date().toISOString();
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1088
1263
|
await this.saveCard(card);
|
|
1264
|
+
await this.handleCardChanged(card);
|
|
1089
1265
|
}
|
|
1090
1266
|
|
|
1091
1267
|
/**
|
|
@@ -1099,21 +1275,15 @@ export class Project extends CardContainer {
|
|
|
1099
1275
|
changedKey: string,
|
|
1100
1276
|
newValue: MetadataContent,
|
|
1101
1277
|
) {
|
|
1102
|
-
const
|
|
1103
|
-
const card = await this.findCard(
|
|
1104
|
-
templateCard ? this.paths.templatesFolder : this.paths.cardRootFolder,
|
|
1105
|
-
cardKey,
|
|
1106
|
-
{
|
|
1107
|
-
metadata: true,
|
|
1108
|
-
},
|
|
1109
|
-
);
|
|
1110
|
-
if (!card) {
|
|
1111
|
-
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1278
|
+
const card = this.findCard(cardKey);
|
|
1114
1279
|
if (!card.metadata || card.metadata[changedKey] === newValue) {
|
|
1115
1280
|
return;
|
|
1116
1281
|
}
|
|
1282
|
+
|
|
1283
|
+
const isRankChange = changedKey === 'rank';
|
|
1284
|
+
const previousPath = isRankChange ? card.path : undefined;
|
|
1285
|
+
const previousParent = isRankChange ? card.parent : undefined;
|
|
1286
|
+
|
|
1117
1287
|
const cardAsRecord: Record<string, MetadataContent> = card.metadata;
|
|
1118
1288
|
cardAsRecord[changedKey] = newValue;
|
|
1119
1289
|
|
|
@@ -1124,35 +1294,70 @@ export class Project extends CardContainer {
|
|
|
1124
1294
|
throw new Error(invalidCard);
|
|
1125
1295
|
}
|
|
1126
1296
|
|
|
1127
|
-
await this.saveCardMetadata(card);
|
|
1297
|
+
const updated = await this.saveCardMetadata(card);
|
|
1298
|
+
if (!updated) return;
|
|
1299
|
+
|
|
1300
|
+
// For rank changes, check if path changed (indicating a move)
|
|
1301
|
+
if (isRankChange) {
|
|
1302
|
+
const updatedCard = this.findCard(cardKey);
|
|
1303
|
+
if (updatedCard.path !== previousPath) {
|
|
1304
|
+
this.changeParent(updatedCard, previousParent);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1128
1307
|
}
|
|
1129
1308
|
|
|
1130
1309
|
/**
|
|
1131
|
-
* Updates card
|
|
1310
|
+
* Updates the entire card in the card cache and handles any path/parent changes.
|
|
1311
|
+
* Also persists changes to content and metadata files.
|
|
1312
|
+
* @param card The card with updated information (path, parent, metadata, etc.)
|
|
1313
|
+
*/
|
|
1314
|
+
public async updateCard(card: Card) {
|
|
1315
|
+
const cachedCard = this.cardCache.getCard(card.key);
|
|
1316
|
+
const pathChange = cachedCard && cachedCard.path !== card.path;
|
|
1317
|
+
|
|
1318
|
+
if (pathChange) {
|
|
1319
|
+
this.changeParent(card, cachedCard.parent);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
const metadataChanged =
|
|
1323
|
+
cachedCard &&
|
|
1324
|
+
JSON.stringify(cachedCard.metadata) !== JSON.stringify(card.metadata);
|
|
1325
|
+
if (metadataChanged) {
|
|
1326
|
+
await this.saveCardMetadata(card);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
const contentChanged = cachedCard && cachedCard.content !== card.content;
|
|
1330
|
+
if (contentChanged) {
|
|
1331
|
+
await this.saveCardContent(card);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
this.cardCache.updateCard(card.key, card);
|
|
1335
|
+
if (metadataChanged || contentChanged || pathChange) {
|
|
1336
|
+
await this.handleCardChanged(card);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Updates a card's metadata.
|
|
1132
1342
|
* @param card affected card
|
|
1133
1343
|
* @param changedMetadata changed content for the card
|
|
1134
1344
|
*/
|
|
1135
1345
|
public async updateCardMetadata(card: Card, changedMetadata: CardMetadata) {
|
|
1136
1346
|
card.metadata = changedMetadata;
|
|
1137
|
-
|
|
1347
|
+
if (await this.saveCardMetadata(card)) {
|
|
1348
|
+
await this.handleCardChanged(card);
|
|
1349
|
+
}
|
|
1138
1350
|
}
|
|
1139
1351
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
* @param card Card to validate.
|
|
1143
|
-
* @returns validation errors, if any
|
|
1144
|
-
*/
|
|
1145
|
-
public async validateCard(card: Card): Promise<string> {
|
|
1352
|
+
// Validates that card's data is valid.
|
|
1353
|
+
private async validateCard(card: Card): Promise<string> {
|
|
1146
1354
|
const invalidCustomData = await this.validator.validateCustomFields(
|
|
1147
1355
|
this,
|
|
1148
1356
|
card,
|
|
1149
1357
|
);
|
|
1150
|
-
const invalidWorkFlow =
|
|
1151
|
-
this,
|
|
1152
|
-
card,
|
|
1153
|
-
);
|
|
1358
|
+
const invalidWorkFlow = this.validator.validateWorkflowState(this, card);
|
|
1154
1359
|
|
|
1155
|
-
const invalidLabels =
|
|
1360
|
+
const invalidLabels = this.validator.validateCardLabels(card);
|
|
1156
1361
|
if (
|
|
1157
1362
|
invalidCustomData.length === 0 &&
|
|
1158
1363
|
invalidWorkFlow.length === 0 &&
|