@cyberismo/data-handler 0.0.14 → 0.0.16
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 +8 -4
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.d.ts +4 -0
- package/dist/command-handler.js +29 -19
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +25 -2
- package/dist/command-manager.js +30 -5
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +45 -93
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.d.ts +1 -15
- package/dist/commands/edit.js +15 -89
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/export.d.ts +11 -2
- package/dist/commands/export.js +58 -58
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.d.ts +9 -1
- package/dist/commands/import.js +17 -11
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/move.d.ts +1 -2
- package/dist/commands/move.js +107 -146
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.d.ts +8 -1
- package/dist/commands/remove.js +17 -48
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.d.ts +4 -9
- package/dist/commands/rename.js +34 -108
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +22 -34
- package/dist/commands/show.js +103 -151
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.d.ts +9 -2
- package/dist/commands/transition.js +49 -44
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.d.ts +18 -12
- package/dist/commands/update.js +34 -18
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +18 -10
- package/dist/commands/validate.js +101 -47
- 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 +13 -4
- package/dist/containers/project/calculation-engine.js +79 -77
- 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/project-paths.d.ts +5 -4
- package/dist/containers/project/project-paths.js +16 -12
- package/dist/containers/project/project-paths.js.map +1 -1
- package/dist/containers/project/resource-cache.d.ts +169 -0
- package/dist/containers/project/resource-cache.js +507 -0
- package/dist/containers/project/resource-cache.js.map +1 -0
- package/dist/containers/project/resource-handler.d.ts +129 -0
- package/dist/containers/project/resource-handler.js +206 -0
- package/dist/containers/project/resource-handler.js.map +1 -0
- package/dist/containers/project.d.ts +114 -195
- package/dist/containers/project.js +425 -535
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +22 -32
- package/dist/containers/template.js +113 -115
- 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 +7 -4
- package/dist/interfaces/folder-content-interfaces.js +3 -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 +7 -5
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +25 -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 +14 -28
- 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/index.d.ts +1 -1
- package/dist/macros/index.js +2 -2
- package/dist/macros/index.js.map +1 -1
- 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 +3 -6
- 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.d.ts +16 -3
- package/dist/module-manager.js +55 -23
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.d.ts +16 -3
- package/dist/project-settings.js +79 -14
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +6 -33
- package/dist/resources/calculation-resource.js +11 -60
- package/dist/resources/calculation-resource.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +10 -22
- package/dist/resources/card-type-resource.js +46 -66
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/create-defaults.d.ts +3 -2
- package/dist/resources/create-defaults.js +3 -2
- package/dist/resources/create-defaults.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +8 -22
- package/dist/resources/field-type-resource.js +35 -60
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +14 -35
- package/dist/resources/file-resource.js +22 -301
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +44 -66
- package/dist/resources/folder-resource.js +102 -149
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +9 -34
- package/dist/resources/graph-model-resource.js +18 -64
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +9 -29
- package/dist/resources/graph-view-resource.js +13 -48
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +9 -23
- package/dist/resources/link-type-resource.js +11 -33
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +10 -23
- package/dist/resources/report-resource.js +20 -67
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +143 -23
- package/dist/resources/resource-object.js +369 -48
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +10 -17
- package/dist/resources/template-resource.js +19 -27
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +9 -25
- package/dist/resources/workflow-resource.js +25 -55
- 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-fact-builder.d.ts +25 -14
- package/dist/utils/clingo-fact-builder.js +27 -5
- package/dist/utils/clingo-fact-builder.js.map +1 -1
- package/dist/utils/clingo-facts.js +14 -7
- 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 +4 -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/resource-utils.d.ts +1 -0
- package/dist/utils/resource-utils.js +2 -1
- package/dist/utils/resource-utils.js.map +1 -1
- package/package.json +11 -11
- package/src/card-metadata-updater.ts +9 -7
- package/src/command-handler.ts +35 -23
- package/src/command-manager.ts +32 -19
- package/src/commands/create.ts +59 -160
- package/src/commands/edit.ts +16 -132
- package/src/commands/export.ts +71 -81
- package/src/commands/import.ts +26 -18
- package/src/commands/move.ts +143 -179
- package/src/commands/remove.ts +20 -59
- package/src/commands/rename.ts +45 -156
- package/src/commands/show.ts +153 -211
- package/src/commands/transition.ts +53 -58
- package/src/commands/update.ts +44 -23
- package/src/commands/validate.ts +108 -82
- package/src/containers/card-container.ts +200 -360
- package/src/containers/project/calculation-engine.ts +81 -105
- package/src/containers/project/card-cache.ts +497 -0
- package/src/containers/project/project-paths.ts +21 -13
- package/src/containers/project/resource-cache.ts +648 -0
- package/src/containers/project/resource-handler.ts +265 -0
- package/src/containers/project.ts +551 -693
- package/src/containers/template.ts +129 -142
- package/src/index.ts +1 -0
- package/src/interfaces/folder-content-interfaces.ts +14 -7
- package/src/interfaces/macros.ts +2 -0
- package/src/interfaces/project-interfaces.ts +14 -7
- package/src/interfaces/resource-interfaces.ts +30 -27
- package/src/macros/createCards/index.ts +1 -12
- package/src/macros/createCards/types.ts +46 -0
- package/src/macros/graph/index.ts +27 -52
- 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/index.ts +2 -2
- package/src/macros/percentage/index.ts +1 -7
- package/src/macros/percentage/types.ts +32 -0
- package/src/macros/report/index.ts +4 -13
- 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 +79 -22
- package/src/project-settings.ts +84 -15
- package/src/resources/calculation-resource.ts +21 -91
- package/src/resources/card-type-resource.ts +74 -109
- package/src/resources/create-defaults.ts +3 -2
- package/src/resources/field-type-resource.ts +61 -104
- package/src/resources/file-resource.ts +33 -441
- package/src/resources/folder-resource.ts +130 -207
- package/src/resources/graph-model-resource.ts +36 -95
- package/src/resources/graph-view-resource.ts +28 -70
- package/src/resources/link-type-resource.ts +23 -53
- package/src/resources/report-resource.ts +34 -96
- package/src/resources/resource-object.ts +511 -66
- package/src/resources/template-resource.ts +32 -44
- package/src/resources/workflow-resource.ts +42 -85
- package/src/utils/card-utils.ts +217 -31
- package/src/utils/clingo-fact-builder.ts +28 -16
- package/src/utils/clingo-facts.ts +16 -7
- package/src/utils/clingo-parser.ts +1 -1
- package/src/utils/constants.ts +6 -0
- package/src/utils/csv.ts +1 -1
- package/src/utils/resource-utils.ts +2 -1
- package/dist/containers/project/resource-collector.d.ts +0 -87
- package/dist/containers/project/resource-collector.js +0 -337
- package/dist/containers/project/resource-collector.js.map +0 -1
- package/src/containers/project/resource-collector.ts +0 -396
|
@@ -12,261 +12,322 @@
|
|
|
12
12
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
13
|
*/
|
|
14
14
|
// node
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
15
|
+
import { basename, join, resolve } from 'node:path';
|
|
16
|
+
import { constants as fsConstants, copyFile, mkdir, unlink, writeFile, } from 'node:fs/promises';
|
|
17
|
+
// base class
|
|
18
|
+
import { CardContainer } from './card-container.js';
|
|
18
19
|
import { CalculationEngine } from './project/calculation-engine.js';
|
|
19
20
|
import { CardLocation, } from '../interfaces/project-interfaces.js';
|
|
20
|
-
import {
|
|
21
|
+
import { pathExists } from '../utils/file-utils.js';
|
|
21
22
|
import { generateRandomString } from '../utils/random.js';
|
|
22
|
-
import { isTemplateCard } from '../utils/card-utils.js';
|
|
23
|
+
import { cardPathParts, isModulePath, isTemplateCard, } from '../utils/card-utils.js';
|
|
23
24
|
import { ProjectConfiguration } from '../project-settings.js';
|
|
24
25
|
import { ProjectPaths } from './project/project-paths.js';
|
|
25
26
|
import { readJsonFile } from '../utils/json.js';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
27
|
+
import { ResourcesFrom } from './project/resource-cache.js';
|
|
28
|
+
import { ResourceHandler } from './project/resource-handler.js';
|
|
28
29
|
import { Validate } from '../commands/validate.js';
|
|
29
|
-
import { CalculationResource } from '../resources/calculation-resource.js';
|
|
30
|
-
import { CardTypeResource } from '../resources/card-type-resource.js';
|
|
31
|
-
import { FieldTypeResource } from '../resources/field-type-resource.js';
|
|
32
|
-
import { GraphModelResource } from '../resources/graph-model-resource.js';
|
|
33
|
-
import { GraphViewResource } from '../resources/graph-view-resource.js';
|
|
34
|
-
import { LinkTypeResource } from '../resources/link-type-resource.js';
|
|
35
|
-
import { ReportResource } from '../resources/report-resource.js';
|
|
36
|
-
import { TemplateResource } from '../resources/template-resource.js';
|
|
37
|
-
import { WorkflowResource } from '../resources/workflow-resource.js';
|
|
38
30
|
import { ContentWatcher } from './project/project-content-watcher.js';
|
|
39
|
-
import {
|
|
31
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
32
|
+
import { ROOT } from '../utils/constants.js';
|
|
40
33
|
// Re-export this, so that classes that use Project do not need to have separate import.
|
|
41
34
|
export { ResourcesFrom };
|
|
42
35
|
/**
|
|
43
36
|
* Represents project folder.
|
|
44
37
|
*/
|
|
45
38
|
export class Project extends CardContainer {
|
|
46
|
-
|
|
39
|
+
options;
|
|
47
40
|
calculationEngine;
|
|
48
|
-
|
|
41
|
+
logger = getChildLogger({ module: 'Project' });
|
|
49
42
|
projectPaths;
|
|
43
|
+
resourceHandler;
|
|
44
|
+
resourceWatcher;
|
|
50
45
|
settings;
|
|
51
46
|
validator;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
super(path, '');
|
|
58
|
-
this.
|
|
47
|
+
constructor(path, options = {
|
|
48
|
+
autoSave: true,
|
|
49
|
+
watchResourceChanges: false,
|
|
50
|
+
}) {
|
|
51
|
+
const settings = new ProjectConfiguration(join(path, '.cards', 'local', Project.projectConfigFileName), options.autoSave ?? true);
|
|
52
|
+
super(path, settings.cardKeyPrefix, '');
|
|
53
|
+
this.options = options;
|
|
54
|
+
this.settings = settings;
|
|
55
|
+
this.logger.info({ path }, 'Initializing project');
|
|
59
56
|
this.calculationEngine = new CalculationEngine(this);
|
|
60
|
-
this.settings = new ProjectConfiguration(join(path, '.cards', 'local', Project.projectConfigFileName));
|
|
61
57
|
this.projectPaths = new ProjectPaths(path);
|
|
62
|
-
this.
|
|
58
|
+
this.resourceHandler = new ResourceHandler(this);
|
|
63
59
|
this.containerName = this.settings.name;
|
|
64
60
|
// todo: implement project validation
|
|
65
61
|
this.validator = Validate.getInstance();
|
|
66
|
-
this.
|
|
62
|
+
this.logger.info({ name: this.containerName }, 'Project initialization complete');
|
|
67
63
|
const ignoreRenameFileChanges = true;
|
|
68
64
|
// Watch changes in .cards if there are multiple instances of Project being
|
|
69
65
|
// run concurrently.
|
|
70
|
-
if (this.watchResourceChanges) {
|
|
66
|
+
if (this.options.watchResourceChanges) {
|
|
71
67
|
this.resourceWatcher = new ContentWatcher(ignoreRenameFileChanges, this.paths.resourcesFolder, (fileName) => {
|
|
72
68
|
void (async () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
resource = pathToResourceName(this, join(this.paths.resourcesFolder, fileName));
|
|
76
|
-
if (!resource) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// it wasn't a resource that changed, so ignore the change
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const resourceName = `${resource.prefix}/${resource.type}/${resource.identifier}`;
|
|
85
|
-
await this.replaceCacheValue(resourceName);
|
|
86
|
-
this.resources.collectLocalResources();
|
|
69
|
+
this.resources.handleFileSystemChange(join(this.paths.resourcesFolder, fileName));
|
|
70
|
+
this.resources.changed();
|
|
87
71
|
})();
|
|
88
72
|
});
|
|
89
73
|
}
|
|
90
74
|
}
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (this.createdResources.has(resourceName)) {
|
|
96
|
-
// First, remove the old version from cache
|
|
97
|
-
this.createdResources.delete(resourceName);
|
|
75
|
+
// Changes a card's parent in the cache and updates all relationships.
|
|
76
|
+
changeParent(updatedCard, previousParent) {
|
|
77
|
+
if (previousParent && previousParent !== ROOT) {
|
|
78
|
+
this.removeCachedChildren(previousParent, updatedCard.key);
|
|
98
79
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.
|
|
80
|
+
if (updatedCard.parent && updatedCard.parent !== ROOT) {
|
|
81
|
+
this.updateCachedChildren(updatedCard.parent, updatedCard);
|
|
82
|
+
}
|
|
83
|
+
this.cardCache.updateCard(updatedCard.key, updatedCard);
|
|
103
84
|
}
|
|
104
85
|
// Finds specific module.
|
|
105
86
|
async findModule(moduleName) {
|
|
106
|
-
|
|
87
|
+
const moduleExists = this.resources.moduleNames().includes(moduleName);
|
|
88
|
+
if (!moduleExists) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
// For modules, we need to construct the local path where the module is stored
|
|
92
|
+
const moduleConfig = this.configuration.modules?.find((module) => module.name === moduleName);
|
|
93
|
+
if (!moduleConfig) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
name: moduleName,
|
|
98
|
+
path: join(this.paths.modulesFolder, moduleConfig.name),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Handles attachment changes after filesystem operations.
|
|
102
|
+
async handleAttachmentChange(cardKey, operation, fileName) {
|
|
103
|
+
if (operation === 'added') {
|
|
104
|
+
this.cardCache.addAttachment(cardKey, fileName);
|
|
105
|
+
}
|
|
106
|
+
else if (operation === 'removed') {
|
|
107
|
+
this.cardCache.deleteAttachment(cardKey, fileName);
|
|
108
|
+
}
|
|
109
|
+
else if (operation === 'refresh') {
|
|
110
|
+
const newAttachments = this.cardCache.getCardAttachments(cardKey);
|
|
111
|
+
if (newAttachments) {
|
|
112
|
+
this.cardCache.updateCardAttachments(cardKey, newAttachments);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Determines the parent card key from a card's filesystem path.
|
|
117
|
+
// todo: could be moved to card-utils
|
|
118
|
+
parentFromPath(cardPath) {
|
|
119
|
+
return cardPathParts(this.projectPrefix, cardPath).parents.at(-1) || 'root';
|
|
120
|
+
}
|
|
121
|
+
// Remove children from a card in the card cache
|
|
122
|
+
removeCachedChildren(parentKey, childKey) {
|
|
123
|
+
const parentCard = this.cardCache.getCard(parentKey);
|
|
124
|
+
if (parentCard && parentCard.children) {
|
|
125
|
+
parentCard.children = parentCard.children.filter((child) => child !== childKey);
|
|
126
|
+
this.cardCache.updateCard(parentCard.key, parentCard);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Updates children in the card cache
|
|
130
|
+
updateCachedChildren(parentKey, newChild) {
|
|
131
|
+
const parentCard = this.cardCache.getCard(parentKey);
|
|
132
|
+
if (parentCard) {
|
|
133
|
+
// Add or update the child in the parent's children array
|
|
134
|
+
const existingChildIndex = parentCard.children?.findIndex((child) => child === newChild.key);
|
|
135
|
+
if (existingChildIndex === -1) {
|
|
136
|
+
parentCard.children.push(newChild.key);
|
|
137
|
+
}
|
|
138
|
+
this.cardCache.updateCard(parentCard.key, parentCard);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Validates that card's data is valid.
|
|
142
|
+
async validateCard(card) {
|
|
143
|
+
const invalidCustomData = await this.validator.validateCustomFields(this, card, this.projectPrefixes());
|
|
144
|
+
const invalidWorkFlow = await this.validator.validateWorkflowState(this, card);
|
|
145
|
+
const invalidLabels = this.validator.validateCardLabels(card);
|
|
146
|
+
if (invalidCustomData.length === 0 &&
|
|
147
|
+
invalidWorkFlow.length === 0 &&
|
|
148
|
+
invalidLabels.length === 0) {
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
const errors = [];
|
|
152
|
+
if (invalidCustomData.length > 0) {
|
|
153
|
+
errors.push(invalidCustomData);
|
|
154
|
+
}
|
|
155
|
+
if (invalidWorkFlow.length > 0) {
|
|
156
|
+
errors.push(invalidWorkFlow);
|
|
157
|
+
}
|
|
158
|
+
if (invalidLabels.length > 0) {
|
|
159
|
+
errors.push(invalidLabels);
|
|
160
|
+
}
|
|
161
|
+
return errors.join('\n');
|
|
107
162
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Populate template cards into the card cache.
|
|
165
|
+
*/
|
|
166
|
+
async populateTemplateCards() {
|
|
167
|
+
try {
|
|
168
|
+
const templateResources = this.resources.templates();
|
|
169
|
+
const prefixes = this.projectPrefixes();
|
|
170
|
+
const loadPromises = templateResources.map(async (template) => {
|
|
171
|
+
try {
|
|
172
|
+
this.validator.validResourceName('templates', template.data?.name || '', prefixes);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
this.logger.warn({ templateName: template, error }, `Template name '${template}' does not follow required format, skipping`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const templateObject = template.templateObject();
|
|
179
|
+
const isCreated = templateObject && templateObject.isCreated();
|
|
180
|
+
if (!templateObject || !isCreated) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
await this.cardCache.populateFromPath(templateObject.templateCardsFolder(), false);
|
|
184
|
+
});
|
|
185
|
+
await Promise.all(loadPromises);
|
|
186
|
+
// Once all templates have been fetched, build child-parent relationships.
|
|
187
|
+
this.cardCache.populateChildrenRelationships();
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
this.logger.error({ error }, 'Failed to populate template cards into the card cache');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Populate both the project cards, and all template cards into card cache.
|
|
195
|
+
*/
|
|
196
|
+
async populateCardsCache() {
|
|
197
|
+
await this.cardCache.populateFromPath(this.paths.cardRootFolder);
|
|
198
|
+
await this.populateTemplateCards();
|
|
112
199
|
}
|
|
113
200
|
/**
|
|
114
|
-
*
|
|
115
|
-
* @
|
|
116
|
-
* @param data JSON data for the resource.
|
|
201
|
+
* Returns all template cards from the project. This includes all module templates' cards.
|
|
202
|
+
* @returns all the template cards from the project
|
|
117
203
|
*/
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
this.createdResources.set(resource.name, data);
|
|
204
|
+
allTemplateCards() {
|
|
205
|
+
return this.cardCache.getAllTemplateCards();
|
|
121
206
|
}
|
|
122
207
|
/**
|
|
123
208
|
* Returns an array of all the attachments in the project card's (excluding ones in templates).
|
|
124
209
|
* @returns all attachments in the project.
|
|
125
210
|
*/
|
|
126
|
-
|
|
211
|
+
attachments() {
|
|
127
212
|
return super.attachments(this.paths.cardRootFolder);
|
|
128
213
|
}
|
|
129
214
|
/**
|
|
130
|
-
* Returns
|
|
131
|
-
*
|
|
132
|
-
* @
|
|
215
|
+
* Returns attachments from cards at a specific path using the card cache.
|
|
216
|
+
* This method allows templates to access attachments from the shared cache.
|
|
217
|
+
* @param path The path to get attachments from
|
|
218
|
+
* @returns Array of attachments from cards at the specified path
|
|
133
219
|
*/
|
|
134
|
-
|
|
135
|
-
return
|
|
220
|
+
attachmentsByPath(path) {
|
|
221
|
+
return super.attachments(path);
|
|
136
222
|
}
|
|
137
223
|
/**
|
|
138
|
-
* Returns path to card's attachment folder.
|
|
224
|
+
* Returns path to a card's attachment folder.
|
|
139
225
|
* @param cardKey card key
|
|
140
|
-
* @returns path to card's attachment folder.
|
|
141
|
-
* @throws if card path cannot be found
|
|
226
|
+
* @returns path to a card's attachment folder.
|
|
142
227
|
*/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const cardPath = await this.cardFolder(cardKey);
|
|
147
|
-
return join(cardPath, 'a');
|
|
148
|
-
}
|
|
149
|
-
const pathToProjectCard = this.pathToCard(cardKey);
|
|
150
|
-
if (!pathToProjectCard) {
|
|
151
|
-
throw new Error(`Card '${cardKey}' not found`);
|
|
152
|
-
}
|
|
153
|
-
return join(this.paths.cardRootFolder, pathToProjectCard, 'a');
|
|
228
|
+
cardAttachmentFolder(cardKey) {
|
|
229
|
+
const pathToCard = this.findCard(cardKey).path;
|
|
230
|
+
return join(pathToCard, 'a');
|
|
154
231
|
}
|
|
155
232
|
/**
|
|
156
|
-
*
|
|
157
|
-
* @param cardKey card
|
|
158
|
-
* @param
|
|
159
|
-
* @
|
|
233
|
+
* Creates an attachment for a card.
|
|
234
|
+
* @param cardKey The card to add attachment to
|
|
235
|
+
* @param attachmentName The name for the attachment file
|
|
236
|
+
* @param attachmentData The attachment data (file path or buffer)
|
|
237
|
+
* @throws If trying to add attachment to module card, or if attachment is not found
|
|
160
238
|
*/
|
|
161
|
-
async
|
|
162
|
-
|
|
239
|
+
async createCardAttachment(cardKey, attachmentName, attachmentData) {
|
|
240
|
+
const attachmentFolder = this.cardAttachmentFolder(cardKey);
|
|
241
|
+
// Check if this is a module template
|
|
242
|
+
if (isModulePath(attachmentFolder)) {
|
|
243
|
+
throw new Error(`Cannot modify imported module`);
|
|
244
|
+
}
|
|
245
|
+
// Create the attachment folder if it doesn't exist
|
|
246
|
+
await mkdir(attachmentFolder, { recursive: true });
|
|
247
|
+
const attachmentPath = join(attachmentFolder, basename(attachmentName));
|
|
248
|
+
if (Buffer.isBuffer(attachmentData)) {
|
|
249
|
+
await writeFile(attachmentPath, attachmentData, { flag: 'wx' });
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
try {
|
|
253
|
+
await copyFile(attachmentData, attachmentPath, fsConstants.COPYFILE_EXCL);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
throw new Error(`Attachment file not found: ${attachmentData}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Update cache
|
|
260
|
+
await this.handleAttachmentChange(cardKey, 'added', basename(attachmentName));
|
|
163
261
|
}
|
|
164
262
|
/**
|
|
165
|
-
* Returns path to card's folder.
|
|
263
|
+
* Returns path to a card's folder.
|
|
166
264
|
* @param cardKey card key
|
|
167
|
-
* @returns path to card's folder.
|
|
265
|
+
* @returns path to a card's folder.
|
|
168
266
|
*/
|
|
169
267
|
async cardFolder(cardKey) {
|
|
170
|
-
const found =
|
|
268
|
+
const found = super.findCard(cardKey);
|
|
171
269
|
if (found) {
|
|
172
270
|
return found.path;
|
|
173
271
|
}
|
|
174
|
-
const templates =
|
|
175
|
-
const templatePromises = templates.map(
|
|
176
|
-
const templateObject =
|
|
272
|
+
const templates = this.resources.templates();
|
|
273
|
+
const templatePromises = templates.map((template) => {
|
|
274
|
+
const templateObject = template.templateObject();
|
|
177
275
|
const templateCard = templateObject
|
|
178
|
-
?
|
|
276
|
+
? templateObject.findCard(cardKey)
|
|
179
277
|
: undefined;
|
|
180
|
-
|
|
278
|
+
const path = templateCard ? templateCard.path : '';
|
|
279
|
+
return path;
|
|
181
280
|
});
|
|
182
281
|
const templatePaths = await Promise.all(templatePromises);
|
|
183
282
|
return templatePaths.find((path) => path !== '') || '';
|
|
184
283
|
}
|
|
185
284
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* @
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const parents = [];
|
|
197
|
-
let prefix = this.projectPrefix;
|
|
198
|
-
let template = '';
|
|
199
|
-
let startIndex = -1;
|
|
200
|
-
let templatesNameIndex = -1;
|
|
201
|
-
const cardRootIndex = pathParts.indexOf('cardRoot');
|
|
202
|
-
const projectInternalsIndex = pathParts.indexOf('.cards');
|
|
203
|
-
if (projectInternalsIndex === -1 && cardRootIndex >= 0) {
|
|
204
|
-
startIndex = projectInternalsIndex;
|
|
205
|
-
}
|
|
206
|
-
else if (projectInternalsIndex >= 0 && cardRootIndex === -1) {
|
|
207
|
-
const templatesIndex = pathParts.indexOf('templates');
|
|
208
|
-
startIndex = templatesIndex;
|
|
209
|
-
if (templatesIndex === -1) {
|
|
210
|
-
throw new Error(`Invalid card path. Template card must have 'templates' in path`);
|
|
211
|
-
}
|
|
212
|
-
const modulesIndex = pathParts.indexOf('modules');
|
|
213
|
-
if (modulesIndex !== -1) {
|
|
214
|
-
prefix = pathParts.at(modulesIndex + 1) || '';
|
|
215
|
-
}
|
|
216
|
-
templatesNameIndex = templatesIndex + 1;
|
|
217
|
-
template = `${prefix}/templates/${pathParts.at(templatesNameIndex)}`;
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
throw new Error(`Card must be either project card, or template card`);
|
|
221
|
-
}
|
|
222
|
-
// Look for parents in the path.
|
|
223
|
-
let previousWasParent = false;
|
|
224
|
-
for (let index = startIndex; index <= pathParts.length; index++) {
|
|
225
|
-
if (previousWasParent) {
|
|
226
|
-
previousWasParent = false;
|
|
227
|
-
parents.push(pathParts.at(index - 2));
|
|
228
|
-
}
|
|
229
|
-
const cardsSubFolder = pathParts.at(index) === 'c';
|
|
230
|
-
const ignoreOrNotTemplatesParent = index - 1 !== templatesNameIndex || templatesNameIndex === -1;
|
|
231
|
-
if (cardsSubFolder && ignoreOrNotTemplatesParent) {
|
|
232
|
-
previousWasParent = true;
|
|
285
|
+
* Fetches full Card data for given card keys
|
|
286
|
+
* @param cardIds array of card keys to fetch
|
|
287
|
+
* @returns Card data to the given card keys
|
|
288
|
+
*/
|
|
289
|
+
cardKeysToCards(cardIds) {
|
|
290
|
+
const cards = [];
|
|
291
|
+
for (const cardId of cardIds) {
|
|
292
|
+
const card = this.cardCache.getCard(cardId);
|
|
293
|
+
if (card) {
|
|
294
|
+
cards.push(card);
|
|
233
295
|
}
|
|
234
296
|
}
|
|
235
|
-
return
|
|
236
|
-
cardKey: cardKey,
|
|
237
|
-
parents: parents,
|
|
238
|
-
prefix: prefix,
|
|
239
|
-
template: template,
|
|
240
|
-
};
|
|
297
|
+
return cards;
|
|
241
298
|
}
|
|
242
299
|
/**
|
|
243
|
-
* Returns an array of all the cards in the project.
|
|
244
|
-
* @
|
|
245
|
-
* @param
|
|
300
|
+
* Returns an array of all the cards in the project.
|
|
301
|
+
* @note These are project cards only, by default (unless path dictates otherwise).
|
|
302
|
+
* @param path Path from which to fetch the cards. Generally it is best to fetch from Project root, e.g. Project.cardRootFolder
|
|
303
|
+
* @param details Which details to include in the cards; by default all details are included.
|
|
246
304
|
* @returns all cards from the given path in the project.
|
|
247
305
|
*/
|
|
248
|
-
|
|
306
|
+
cards(path = this.paths.cardRootFolder, details) {
|
|
249
307
|
return super.cards(path, details);
|
|
250
308
|
}
|
|
251
309
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* @
|
|
310
|
+
* Accessor for cards cache.
|
|
311
|
+
* Used by template container (it needs to access project's cache, not their own instance).
|
|
312
|
+
* @note Should not be used directly (other than Template).
|
|
255
313
|
*/
|
|
256
|
-
|
|
257
|
-
return this.
|
|
314
|
+
get cardsCache() {
|
|
315
|
+
return this.cardCache;
|
|
258
316
|
}
|
|
259
317
|
/**
|
|
260
|
-
*
|
|
318
|
+
* Returns children of a given card; as Card array
|
|
319
|
+
* @param card Parent card to fetch children from
|
|
320
|
+
* @returns children of a given card; as Card array
|
|
261
321
|
*/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
322
|
+
childrenCards(card) {
|
|
323
|
+
const cards = [];
|
|
324
|
+
for (const child of card.children) {
|
|
325
|
+
const card = this.cardCache.getCard(child);
|
|
326
|
+
if (card) {
|
|
327
|
+
cards.push(card);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return cards;
|
|
270
331
|
}
|
|
271
332
|
/**
|
|
272
333
|
* Returns project configuration.
|
|
@@ -284,8 +345,9 @@ export class Project extends CardContainer {
|
|
|
284
345
|
if (!card || !card.path || !isTemplateCard(card)) {
|
|
285
346
|
return undefined;
|
|
286
347
|
}
|
|
287
|
-
const { template } = this.
|
|
288
|
-
|
|
348
|
+
const { template } = cardPathParts(this.projectPrefix, card.path);
|
|
349
|
+
const templateResource = this.resources.byType(template, 'templates');
|
|
350
|
+
return templateResource.templateObject();
|
|
289
351
|
}
|
|
290
352
|
/**
|
|
291
353
|
* Cleanups project when it is being closed.
|
|
@@ -297,12 +359,13 @@ export class Project extends CardContainer {
|
|
|
297
359
|
}
|
|
298
360
|
}
|
|
299
361
|
/**
|
|
300
|
-
* Returns
|
|
301
|
-
* @param
|
|
302
|
-
* @
|
|
362
|
+
* Returns specific card.
|
|
363
|
+
* @param cardToFind Card key to find
|
|
364
|
+
* @param details Defines which card details are included in the return values.
|
|
365
|
+
* @returns specific card details, or undefined if card is not part of the project.
|
|
303
366
|
*/
|
|
304
|
-
|
|
305
|
-
return
|
|
367
|
+
findCard(cardToFind, details) {
|
|
368
|
+
return super.findCard(cardToFind, details);
|
|
306
369
|
}
|
|
307
370
|
/**
|
|
308
371
|
* Finds root of a project
|
|
@@ -320,128 +383,105 @@ export class Project extends CardContainer {
|
|
|
320
383
|
}
|
|
321
384
|
return Project.findProjectRoot(parentPath);
|
|
322
385
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Returns specific card.
|
|
325
|
-
* @param cardToFind Card key to find
|
|
326
|
-
* @param details Defines which card details are included in the return values.
|
|
327
|
-
* @returns specific card details, or undefined if card is not part of the project.
|
|
328
|
-
*/
|
|
329
|
-
async findSpecificCard(cardToFind, details = {}) {
|
|
330
|
-
let card;
|
|
331
|
-
if (details.location === CardLocation.projectOnly ||
|
|
332
|
-
details.location === CardLocation.all ||
|
|
333
|
-
!details.location) {
|
|
334
|
-
card = await super.findCard(this.paths.cardRootFolder, cardToFind, details);
|
|
335
|
-
}
|
|
336
|
-
if (!card &&
|
|
337
|
-
(details.location === CardLocation.templatesOnly ||
|
|
338
|
-
details.location === CardLocation.all ||
|
|
339
|
-
!details.location)) {
|
|
340
|
-
const templates = await this.templates();
|
|
341
|
-
for (const template of templates) {
|
|
342
|
-
const templateObject = new TemplateResource(this, resourceName(template.name)).templateObject();
|
|
343
|
-
if (!templateObject)
|
|
344
|
-
continue;
|
|
345
|
-
// optimize: execute each find in template parallel
|
|
346
|
-
card = await templateObject.findSpecificCard(cardToFind, details);
|
|
347
|
-
if (card) {
|
|
348
|
-
break;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return card;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Returns an array of all the graph models in the project.
|
|
356
|
-
* @param from Defines where resources are collected from.
|
|
357
|
-
* @returns array of all the graph models in the project.
|
|
358
|
-
*/
|
|
359
|
-
async graphModels(from = ResourcesFrom.all) {
|
|
360
|
-
return this.resources.resources('graphModels', from);
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Returns an array of all the graph views in the project.
|
|
364
|
-
* @param from Defines where resources are collected from.
|
|
365
|
-
* @returns array of all the graph views in the project.
|
|
366
|
-
*/
|
|
367
|
-
async graphViews(from = ResourcesFrom.all) {
|
|
368
|
-
return this.resources.resources('graphViews', from);
|
|
369
|
-
}
|
|
370
386
|
/**
|
|
371
387
|
* When card changes.
|
|
372
388
|
* @param changedCard Card that was changed.
|
|
373
389
|
*/
|
|
374
390
|
async handleCardChanged(changedCard) {
|
|
391
|
+
// Notify the calculation engine about the change
|
|
375
392
|
return this.calculationEngine.handleCardChanged(changedCard);
|
|
376
393
|
}
|
|
377
394
|
/**
|
|
378
395
|
* When cards are removed.
|
|
379
396
|
* @param deletedCard Card that is to be removed.
|
|
380
397
|
*/
|
|
381
|
-
async
|
|
398
|
+
async handleCardDeleted(deletedCard) {
|
|
399
|
+
// Delete children from the cache first
|
|
400
|
+
if (deletedCard.children && deletedCard.children.length > 0) {
|
|
401
|
+
for (const child of deletedCard.children) {
|
|
402
|
+
try {
|
|
403
|
+
const childCard = this.findCard(child);
|
|
404
|
+
await this.handleCardDeleted(childCard);
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
this.logger.warn(`Accessing child '${child}' of '${deletedCard.key}' when deleting cards caused an exception`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
await super.removeCard(deletedCard.key);
|
|
382
413
|
return this.calculationEngine.handleDeleteCard(deletedCard);
|
|
383
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* When card is moved.
|
|
417
|
+
* @param movedCard Card that moved
|
|
418
|
+
* @param newParentCard New parent for the 'movedCard'
|
|
419
|
+
* @param oldParentCard Previous parent of the 'movedCard'
|
|
420
|
+
*/
|
|
421
|
+
async handleCardMoved(movedCard, newParentCard, oldParentCard) {
|
|
422
|
+
if (newParentCard) {
|
|
423
|
+
this.cardCache.updateCard(newParentCard.key, newParentCard);
|
|
424
|
+
}
|
|
425
|
+
if (oldParentCard) {
|
|
426
|
+
this.cardCache.updateCard(oldParentCard.key, oldParentCard);
|
|
427
|
+
}
|
|
428
|
+
this.cardCache.updateCard(movedCard.key, movedCard);
|
|
429
|
+
// todo: it would be enough to just update parent, previous parent and changed card
|
|
430
|
+
this.cardCache.populateChildrenRelationships();
|
|
431
|
+
await this.handleCardChanged(movedCard);
|
|
432
|
+
await this.calculationEngine.handleCardMoved();
|
|
433
|
+
}
|
|
384
434
|
/**
|
|
385
435
|
* When new cards are added.
|
|
386
436
|
* @param cards Added cards.
|
|
387
437
|
*/
|
|
388
438
|
async handleNewCards(cards) {
|
|
439
|
+
// Add new cards to the card cache
|
|
440
|
+
cards.forEach((card) => {
|
|
441
|
+
const cardWithParent = {
|
|
442
|
+
...card,
|
|
443
|
+
parent: card.parent || this.parentFromPath(card.path),
|
|
444
|
+
};
|
|
445
|
+
this.cardCache.updateCard(cardWithParent.key, cardWithParent);
|
|
446
|
+
// Update the parent's children list in the cache
|
|
447
|
+
if (cardWithParent.parent && cardWithParent.parent !== ROOT) {
|
|
448
|
+
this.updateCachedChildren(cardWithParent.parent, cardWithParent);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
389
451
|
return this.calculationEngine.handleNewCards(cards);
|
|
390
452
|
}
|
|
391
|
-
/**
|
|
392
|
-
* Checks if a given card is part of this project.
|
|
393
|
-
* @param cardKey card to check.
|
|
394
|
-
* @returns true if a given card is found from project, false otherwise.
|
|
395
|
-
*/
|
|
396
|
-
hasCard(cardKey) {
|
|
397
|
-
return super.hasCard(cardKey, this.paths.cardRootFolder);
|
|
398
|
-
}
|
|
399
453
|
/**
|
|
400
454
|
* Adds a module from project.
|
|
401
|
-
* @param module
|
|
455
|
+
* @param module Module to add
|
|
402
456
|
*/
|
|
403
457
|
async importModule(module) {
|
|
404
458
|
// Add module as a dependency.
|
|
405
459
|
await this.configuration.addModule(module);
|
|
406
|
-
|
|
460
|
+
this.resources.changedModules();
|
|
461
|
+
await this.populateTemplateCards();
|
|
462
|
+
this.logger.info(`Imported module '${module.name}'`);
|
|
407
463
|
}
|
|
408
464
|
/**
|
|
409
|
-
* Checks if given path is a project.
|
|
465
|
+
* Checks if a given path is a project.
|
|
410
466
|
* @param path Path to a project
|
|
411
467
|
* @returns true, if in the given path there is a project; false otherwise
|
|
412
468
|
*/
|
|
413
469
|
static isCreated(path) {
|
|
414
470
|
return pathExists(join(path, 'cardRoot'));
|
|
415
471
|
}
|
|
416
|
-
/**
|
|
417
|
-
* Returns whether card is a template card or not
|
|
418
|
-
* @param cardKey card to check.
|
|
419
|
-
* @todo: This is only used from 'remove'. Could it use the static checker?
|
|
420
|
-
* @returns true, if card is template card; false otherwise
|
|
421
|
-
*/
|
|
422
|
-
async isTemplateCard(cardKey) {
|
|
423
|
-
const templateCards = await this.allTemplateCards();
|
|
424
|
-
return templateCards.find((card) => card.key === cardKey) != null;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Returns an array of all the link types in the project.
|
|
428
|
-
* @param from Defines where resources are collected from.
|
|
429
|
-
* @returns array of all link types in the project.
|
|
430
|
-
*/
|
|
431
|
-
async linkTypes(from = ResourcesFrom.all) {
|
|
432
|
-
return this.resources.resources('linkTypes', from);
|
|
433
|
-
}
|
|
434
472
|
/**
|
|
435
473
|
* Returns an array of cards in the project, in the templates or both.
|
|
436
474
|
* Cards don't have content and nor metadata.
|
|
437
475
|
* @param cardsFrom Where to return cards from (project, templates, or both)
|
|
438
|
-
* @returns all cards in the project.
|
|
476
|
+
* @returns all cards in the project per container.
|
|
439
477
|
*/
|
|
440
478
|
async listCards(cardsFrom = CardLocation.all) {
|
|
441
479
|
const cardListContainer = [];
|
|
442
480
|
if (cardsFrom === CardLocation.all ||
|
|
443
481
|
cardsFrom === CardLocation.projectOnly) {
|
|
444
|
-
const projectCards =
|
|
482
|
+
const projectCards = super
|
|
483
|
+
.cards(this.paths.cardRootFolder)
|
|
484
|
+
.map((item) => item.key);
|
|
445
485
|
cardListContainer.push({
|
|
446
486
|
name: this.projectName,
|
|
447
487
|
type: 'project',
|
|
@@ -450,15 +490,15 @@ export class Project extends CardContainer {
|
|
|
450
490
|
}
|
|
451
491
|
if (cardsFrom === CardLocation.all ||
|
|
452
492
|
cardsFrom === CardLocation.templatesOnly) {
|
|
453
|
-
const templates =
|
|
493
|
+
const templates = this.resources.templates();
|
|
454
494
|
for (const template of templates) {
|
|
455
|
-
const templateObject =
|
|
495
|
+
const templateObject = template.templateObject();
|
|
456
496
|
if (templateObject) {
|
|
457
497
|
// todo: optimization - do all this in parallel
|
|
458
|
-
const templateCards =
|
|
459
|
-
if (templateCards) {
|
|
498
|
+
const templateCards = templateObject.listCards();
|
|
499
|
+
if (templateCards.length) {
|
|
460
500
|
cardListContainer.push({
|
|
461
|
-
name: template.name,
|
|
501
|
+
name: template.data?.name || '',
|
|
462
502
|
type: 'template',
|
|
463
503
|
cards: templateCards.map((item) => item.key),
|
|
464
504
|
});
|
|
@@ -475,89 +515,43 @@ export class Project extends CardContainer {
|
|
|
475
515
|
* @note that cardIDs are not sorted.
|
|
476
516
|
*/
|
|
477
517
|
async listCardIds(cardsFrom = CardLocation.all) {
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
.then((cards) => new Set(cards.map((card) => card.key))));
|
|
518
|
+
const cardContainers = await this.listCards(cardsFrom);
|
|
519
|
+
const allCardIDs = new Set();
|
|
520
|
+
for (const container of cardContainers) {
|
|
521
|
+
const cards = container.cards;
|
|
522
|
+
cards.forEach((card) => allCardIDs.add(card));
|
|
484
523
|
}
|
|
485
|
-
|
|
486
|
-
cardsFrom === CardLocation.templatesOnly) {
|
|
487
|
-
promises.push((async () => {
|
|
488
|
-
const templates = await this.templates();
|
|
489
|
-
const templateResources = templates.map((template) => new TemplateResource(this, resourceName(template.name)));
|
|
490
|
-
const templateObjectsResults = await Promise.allSettled(templateResources);
|
|
491
|
-
const templateObjects = templateObjectsResults
|
|
492
|
-
.filter((result) => result.status === 'fulfilled' && result.value !== null)
|
|
493
|
-
.map((result) => result.value);
|
|
494
|
-
const listCardsResults = await Promise.allSettled(templateObjects.map((obj) => obj.templateObject().listCards()));
|
|
495
|
-
const templateCardIds = new Set();
|
|
496
|
-
listCardsResults
|
|
497
|
-
.filter((result) => result.status === 'fulfilled')
|
|
498
|
-
.forEach((result) => {
|
|
499
|
-
result.value.forEach((card) => templateCardIds.add(card.key));
|
|
500
|
-
});
|
|
501
|
-
return templateCardIds;
|
|
502
|
-
})());
|
|
503
|
-
}
|
|
504
|
-
const allCardIdSets = await Promise.all(promises);
|
|
505
|
-
return new Set(allCardIdSets.flatMap((set) => [...set]));
|
|
524
|
+
return allCardIDs;
|
|
506
525
|
}
|
|
507
526
|
/**
|
|
508
527
|
* Returns details of a certain module.
|
|
509
528
|
* @param moduleName Name of the module.
|
|
510
|
-
* @returns module details, or undefined if
|
|
529
|
+
* @returns module details, or undefined if module cannot be found.
|
|
511
530
|
*/
|
|
512
531
|
async module(moduleName) {
|
|
513
532
|
const module = await this.findModule(moduleName);
|
|
514
533
|
if (module && module.path) {
|
|
515
|
-
const modulePath =
|
|
516
|
-
const moduleConfig =
|
|
534
|
+
const modulePath = module.path;
|
|
535
|
+
const moduleConfig = await readJsonFile(join(modulePath, Project.projectConfigFileName));
|
|
517
536
|
return {
|
|
518
537
|
name: moduleConfig.name,
|
|
519
538
|
modules: moduleConfig.modules,
|
|
520
539
|
hubs: moduleConfig.hubs,
|
|
521
540
|
path: modulePath,
|
|
522
541
|
cardKeyPrefix: moduleConfig.cardKeyPrefix,
|
|
523
|
-
calculations:
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
graphModels: [
|
|
533
|
-
...(await this.resources.collectResourcesFromModules('graphModels', moduleName)),
|
|
534
|
-
],
|
|
535
|
-
graphViews: [
|
|
536
|
-
...(await this.resources.collectResourcesFromModules('graphViews', moduleName)),
|
|
537
|
-
],
|
|
538
|
-
linkTypes: [
|
|
539
|
-
...(await this.resources.collectResourcesFromModules('linkTypes', moduleName)),
|
|
540
|
-
],
|
|
541
|
-
reports: [
|
|
542
|
-
...(await this.resources.collectResourcesFromModules('reports', moduleName)),
|
|
543
|
-
],
|
|
544
|
-
templates: [
|
|
545
|
-
...(await this.resources.collectResourcesFromModules('templates', moduleName)),
|
|
546
|
-
],
|
|
547
|
-
workflows: [
|
|
548
|
-
...(await this.resources.collectResourcesFromModules('workflows', moduleName)),
|
|
549
|
-
],
|
|
542
|
+
calculations: this.resources.moduleResourceNames('calculations', moduleName),
|
|
543
|
+
cardTypes: this.resources.moduleResourceNames('cardTypes', moduleName),
|
|
544
|
+
fieldTypes: this.resources.moduleResourceNames('fieldTypes', moduleName),
|
|
545
|
+
graphModels: this.resources.moduleResourceNames('graphModels', moduleName),
|
|
546
|
+
graphViews: this.resources.moduleResourceNames('graphViews', moduleName),
|
|
547
|
+
linkTypes: this.resources.moduleResourceNames('linkTypes', moduleName),
|
|
548
|
+
reports: this.resources.moduleResourceNames('reports', moduleName),
|
|
549
|
+
templates: this.resources.moduleResourceNames('templates', moduleName),
|
|
550
|
+
workflows: this.resources.moduleResourceNames('workflows', moduleName),
|
|
550
551
|
};
|
|
551
552
|
}
|
|
552
553
|
return undefined;
|
|
553
554
|
}
|
|
554
|
-
/**
|
|
555
|
-
* Returns list of modules in the project.
|
|
556
|
-
* @returns list of modules in the project.
|
|
557
|
-
*/
|
|
558
|
-
async modules() {
|
|
559
|
-
return this.resources.resources('modules');
|
|
560
|
-
}
|
|
561
555
|
/**
|
|
562
556
|
* Returns a new unique card key with project prefix (e.g. test_x649it4x).
|
|
563
557
|
* Random part of string will be always 8 characters in base-36 (0-9a-z)
|
|
@@ -624,15 +618,16 @@ export class Project extends CardContainer {
|
|
|
624
618
|
return this.projectPaths;
|
|
625
619
|
}
|
|
626
620
|
/**
|
|
627
|
-
*
|
|
628
|
-
* @param cardKey card to check path for.
|
|
629
|
-
* @returns path to a given card.
|
|
621
|
+
* Populates the card cache, if it has not been populated.
|
|
630
622
|
*/
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
623
|
+
async populateCaches() {
|
|
624
|
+
if (!this.cardCache.isPopulated) {
|
|
625
|
+
// Only collect modules that are registered in the project configuration
|
|
626
|
+
if (this.configuration.modules && this.configuration.modules.length > 0) {
|
|
627
|
+
this.resources.changedModules();
|
|
628
|
+
}
|
|
629
|
+
await this.populateCardsCache();
|
|
630
|
+
}
|
|
636
631
|
}
|
|
637
632
|
/**
|
|
638
633
|
* Returns project name.
|
|
@@ -647,144 +642,60 @@ export class Project extends CardContainer {
|
|
|
647
642
|
return this.settings.cardKeyPrefix;
|
|
648
643
|
}
|
|
649
644
|
/**
|
|
650
|
-
*
|
|
645
|
+
* Returns all prefixes used in the project (project's own plus all from imported modules).
|
|
651
646
|
* @returns all prefixes used in the project.
|
|
652
|
-
* @todo - move the module prefix fetch to resource-collector.
|
|
653
|
-
* Make it use cached value that is only changed when module is removed/imported.
|
|
654
647
|
*/
|
|
655
|
-
|
|
648
|
+
projectPrefixes() {
|
|
656
649
|
const prefixes = [this.projectPrefix];
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
files = await readdir(this.paths.modulesFolder, {
|
|
660
|
-
withFileTypes: true,
|
|
661
|
-
recursive: true,
|
|
662
|
-
});
|
|
663
|
-
const configurationFiles = files
|
|
664
|
-
.filter((dirent) => dirent.isFile())
|
|
665
|
-
.filter((dirent) => dirent.name === Project.projectConfigFileName);
|
|
666
|
-
const configurationPromises = configurationFiles.map(async (file) => {
|
|
667
|
-
const configuration = (await readJsonFile(join(file.parentPath, file.name)));
|
|
668
|
-
return configuration.cardKeyPrefix;
|
|
669
|
-
});
|
|
670
|
-
const configurationPrefixes = await Promise.all(configurationPromises);
|
|
671
|
-
prefixes.push(...configurationPrefixes);
|
|
672
|
-
}
|
|
673
|
-
catch {
|
|
674
|
-
// do nothing if readdir throws // TODO: Log it
|
|
675
|
-
}
|
|
650
|
+
const moduleNames = this.configuration.modules.map((item) => item.name);
|
|
651
|
+
prefixes.push(...moduleNames);
|
|
676
652
|
return prefixes;
|
|
677
653
|
}
|
|
678
654
|
/**
|
|
679
|
-
*
|
|
680
|
-
* @param
|
|
681
|
-
* @
|
|
655
|
+
* Removes an attachment from a card.
|
|
656
|
+
* @param cardKey The card to remove attachment from
|
|
657
|
+
* @param fileName The name of the attachment file to remove
|
|
658
|
+
* @throws if trying to remove module card attachment, or the attachment was not found.
|
|
682
659
|
*/
|
|
683
|
-
async
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
* @param from Defines where report handlebar files are collected from.
|
|
689
|
-
* @returns handlebar files from reports.
|
|
690
|
-
*/
|
|
691
|
-
async reportHandlerBarFiles(from = ResourcesFrom.all) {
|
|
692
|
-
const reports = await this.reports(from);
|
|
693
|
-
const handleBarFiles = [];
|
|
694
|
-
for (const reportResourceName of reports) {
|
|
695
|
-
const name = resourceName(reportResourceName.name);
|
|
696
|
-
const report = new ReportResource(this, name);
|
|
697
|
-
handleBarFiles.push(...(await report.handleBarFiles()));
|
|
660
|
+
async removeCardAttachment(cardKey, fileName) {
|
|
661
|
+
const attachmentFolder = this.cardAttachmentFolder(cardKey);
|
|
662
|
+
// Modules cannot be modified.
|
|
663
|
+
if (isModulePath(attachmentFolder)) {
|
|
664
|
+
throw new Error(`Cannot modify imported module`);
|
|
698
665
|
}
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Removes a resource from Project.
|
|
703
|
-
* @param resource Resource to remove.
|
|
704
|
-
*/
|
|
705
|
-
removeResource(resource) {
|
|
706
|
-
this.resources.remove(resource);
|
|
707
|
-
this.createdResources.delete(resource.name);
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Returns metadata from a given resource
|
|
711
|
-
* @param name Name of a resource
|
|
712
|
-
* @returns Metadata from the resource.
|
|
713
|
-
*/
|
|
714
|
-
async resource(name) {
|
|
715
|
-
const resName = resourceName(name);
|
|
716
|
-
if (this.createdResources.has(resourceNameToString(resName))) {
|
|
717
|
-
const value = this.createdResources.get(resourceNameToString(resName));
|
|
718
|
-
return value;
|
|
719
|
-
}
|
|
720
|
-
let resource = undefined;
|
|
666
|
+
const attachmentPath = join(attachmentFolder, fileName);
|
|
721
667
|
try {
|
|
722
|
-
|
|
668
|
+
await unlink(attachmentPath);
|
|
723
669
|
}
|
|
724
|
-
catch {
|
|
725
|
-
|
|
670
|
+
catch (error) {
|
|
671
|
+
this.logger.error({ error }, 'Removing card attachment');
|
|
672
|
+
throw new Error(`Attachment not found: ${fileName}`);
|
|
726
673
|
}
|
|
727
|
-
|
|
728
|
-
if (!data) {
|
|
729
|
-
return undefined;
|
|
730
|
-
}
|
|
731
|
-
return data;
|
|
674
|
+
await this.handleAttachmentChange(cardKey, 'removed', fileName);
|
|
732
675
|
}
|
|
733
676
|
/**
|
|
734
|
-
*
|
|
677
|
+
* Removes a module from the project cache and configuration.
|
|
678
|
+
* @note that ModuleManager removes the actual files.
|
|
679
|
+
* @param moduleName Module name to remove.
|
|
735
680
|
*/
|
|
736
|
-
|
|
737
|
-
|
|
681
|
+
async removeModule(moduleName) {
|
|
682
|
+
const toBeRemovedTemplates = this.resources.moduleResourceNames('templates', moduleName);
|
|
683
|
+
// First, remove template cards from the cache that are part of removed templates.
|
|
684
|
+
for (const templateName of toBeRemovedTemplates) {
|
|
685
|
+
this.cardCache.deleteCardsFromTemplate(templateName);
|
|
686
|
+
}
|
|
687
|
+
// Then, remove all module resources from cache
|
|
688
|
+
this.resources.removeModule(moduleName);
|
|
689
|
+
// Finally, remove module from project configuration
|
|
690
|
+
await this.configuration.removeModule(moduleName);
|
|
691
|
+
this.logger.info(`Removed module '${moduleName}'`);
|
|
738
692
|
}
|
|
739
693
|
/**
|
|
740
|
-
*
|
|
741
|
-
* @
|
|
742
|
-
* @param name Valid name of resource.
|
|
743
|
-
* @returns boolean, true if resource exists; false otherwise.
|
|
694
|
+
* Accessor for resource handler.
|
|
695
|
+
* @returns Resource handler instance.
|
|
744
696
|
*/
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const resource = resources.find((item) => item.name === name);
|
|
748
|
-
return resource !== undefined;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Instantiates resource object from project with a resource name.
|
|
752
|
-
* @note that this is memory based object only.
|
|
753
|
-
* To manipulate the resource (create files, delete files etc), use the resource object's API.
|
|
754
|
-
* @param project Project from which resources are created from.
|
|
755
|
-
* @param name Resource name
|
|
756
|
-
* @throws if called with unsupported resource type.
|
|
757
|
-
* @returns Created resource.
|
|
758
|
-
*/
|
|
759
|
-
static resourceObject(project, name) {
|
|
760
|
-
if (name.type === 'calculations') {
|
|
761
|
-
return new CalculationResource(project, name);
|
|
762
|
-
}
|
|
763
|
-
else if (name.type === 'cardTypes') {
|
|
764
|
-
return new CardTypeResource(project, name);
|
|
765
|
-
}
|
|
766
|
-
else if (name.type === 'fieldTypes') {
|
|
767
|
-
return new FieldTypeResource(project, name);
|
|
768
|
-
}
|
|
769
|
-
else if (name.type === 'graphModels') {
|
|
770
|
-
return new GraphModelResource(project, name);
|
|
771
|
-
}
|
|
772
|
-
else if (name.type === 'graphViews') {
|
|
773
|
-
return new GraphViewResource(project, name);
|
|
774
|
-
}
|
|
775
|
-
else if (name.type === 'linkTypes') {
|
|
776
|
-
return new LinkTypeResource(project, name);
|
|
777
|
-
}
|
|
778
|
-
else if (name.type === 'reports') {
|
|
779
|
-
return new ReportResource(project, name);
|
|
780
|
-
}
|
|
781
|
-
else if (name.type === 'templates') {
|
|
782
|
-
return new TemplateResource(project, name);
|
|
783
|
-
}
|
|
784
|
-
else if (name.type === 'workflows') {
|
|
785
|
-
return new WorkflowResource(project, name);
|
|
786
|
-
}
|
|
787
|
-
throw new Error(`Unsupported resource type '${resourceNameToString(name)}'`);
|
|
697
|
+
get resources() {
|
|
698
|
+
return this.resourceHandler;
|
|
788
699
|
}
|
|
789
700
|
/**
|
|
790
701
|
* Shows details of a project.
|
|
@@ -796,7 +707,7 @@ export class Project extends CardContainer {
|
|
|
796
707
|
path: this.basePath,
|
|
797
708
|
prefix: this.projectPrefix,
|
|
798
709
|
hubs: this.configuration.hubs,
|
|
799
|
-
modules:
|
|
710
|
+
modules: this.resources.moduleNames(),
|
|
800
711
|
numberOfCards: (await this.listCards(CardLocation.projectOnly))[0].cards
|
|
801
712
|
.length,
|
|
802
713
|
};
|
|
@@ -805,57 +716,37 @@ export class Project extends CardContainer {
|
|
|
805
716
|
* Show cards of a project.
|
|
806
717
|
* @returns an array of all project cards in the project.
|
|
807
718
|
*/
|
|
808
|
-
|
|
719
|
+
showProjectCards() {
|
|
809
720
|
return this.showCards(this.paths.cardRootFolder);
|
|
810
721
|
}
|
|
811
|
-
/**
|
|
812
|
-
* Returns all template cards from the project. This includes all module templates' cards.
|
|
813
|
-
* @param cardDetails which details to fetch. Optional.
|
|
814
|
-
* @returns all the template cards from the project
|
|
815
|
-
*/
|
|
816
|
-
async allTemplateCards(cardDetails) {
|
|
817
|
-
const templates = await this.templates();
|
|
818
|
-
const cards = [];
|
|
819
|
-
for (const template of templates) {
|
|
820
|
-
const templateCards = await this.templateCards(template.name, cardDetails);
|
|
821
|
-
if (templateCards)
|
|
822
|
-
cards.push(...templateCards);
|
|
823
|
-
}
|
|
824
|
-
return cards;
|
|
825
|
-
}
|
|
826
722
|
/**
|
|
827
723
|
* Returns cards from single template.
|
|
828
|
-
* @param templateName Name of the template
|
|
829
|
-
* @param cardDetails Card information
|
|
724
|
+
* @param templateName Name of the template (supports both full names like 'decision/templates/decision' and short names like 'decision')
|
|
830
725
|
* @returns List of cards from template.
|
|
831
726
|
*/
|
|
832
|
-
|
|
833
|
-
const
|
|
834
|
-
return
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
*/
|
|
841
|
-
async templates(from = ResourcesFrom.all) {
|
|
842
|
-
return this.resources.resources('templates', from);
|
|
727
|
+
templateCards(templateName) {
|
|
728
|
+
const templateCards = this.cardCache.getAllTemplateCards();
|
|
729
|
+
return templateCards.filter((cachedCard) => {
|
|
730
|
+
if (cachedCard.location === 'project') {
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
return cachedCard.location === templateName;
|
|
734
|
+
});
|
|
843
735
|
}
|
|
844
736
|
/**
|
|
845
|
-
* Update card content.
|
|
846
|
-
* @param cardKey card
|
|
737
|
+
* Update a card's content.
|
|
738
|
+
* @param cardKey card key to update.
|
|
847
739
|
* @param content changed content
|
|
848
740
|
*/
|
|
849
741
|
async updateCardContent(cardKey, content) {
|
|
850
|
-
const card =
|
|
851
|
-
metadata: true,
|
|
852
|
-
content: true,
|
|
853
|
-
});
|
|
854
|
-
if (!card) {
|
|
855
|
-
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
856
|
-
}
|
|
742
|
+
const card = this.findCard(cardKey);
|
|
857
743
|
card.content = content;
|
|
744
|
+
// Update lastUpdated timestamp in metadata
|
|
745
|
+
if (card.metadata) {
|
|
746
|
+
card.metadata.lastUpdated = new Date().toISOString();
|
|
747
|
+
}
|
|
858
748
|
await this.saveCard(card);
|
|
749
|
+
await this.handleCardChanged(card);
|
|
859
750
|
}
|
|
860
751
|
/**
|
|
861
752
|
* Updates card metadata's single key.
|
|
@@ -864,16 +755,13 @@ export class Project extends CardContainer {
|
|
|
864
755
|
* @param newValue changed value for the key
|
|
865
756
|
*/
|
|
866
757
|
async updateCardMetadataKey(cardKey, changedKey, newValue) {
|
|
867
|
-
const
|
|
868
|
-
const card = await this.findCard(templateCard ? this.paths.templatesFolder : this.paths.cardRootFolder, cardKey, {
|
|
869
|
-
metadata: true,
|
|
870
|
-
});
|
|
871
|
-
if (!card) {
|
|
872
|
-
throw new Error(`Card '${cardKey}' does not exist in the project`);
|
|
873
|
-
}
|
|
758
|
+
const card = this.findCard(cardKey);
|
|
874
759
|
if (!card.metadata || card.metadata[changedKey] === newValue) {
|
|
875
760
|
return;
|
|
876
761
|
}
|
|
762
|
+
const isRankChange = changedKey === 'rank';
|
|
763
|
+
const previousPath = isRankChange ? card.path : undefined;
|
|
764
|
+
const previousParent = isRankChange ? card.parent : undefined;
|
|
877
765
|
const cardAsRecord = card.metadata;
|
|
878
766
|
cardAsRecord[changedKey] = newValue;
|
|
879
767
|
const invalidCard = isTemplateCard(card)
|
|
@@ -882,50 +770,52 @@ export class Project extends CardContainer {
|
|
|
882
770
|
if (invalidCard.length !== 0) {
|
|
883
771
|
throw new Error(invalidCard);
|
|
884
772
|
}
|
|
885
|
-
await this.saveCardMetadata(card);
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
773
|
+
const updated = await this.saveCardMetadata(card);
|
|
774
|
+
if (!updated)
|
|
775
|
+
return;
|
|
776
|
+
// For rank changes, check if path changed (indicating a move)
|
|
777
|
+
if (isRankChange) {
|
|
778
|
+
const updatedCard = this.findCard(cardKey);
|
|
779
|
+
if (updatedCard.path !== previousPath) {
|
|
780
|
+
this.changeParent(updatedCard, previousParent);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
895
783
|
}
|
|
896
784
|
/**
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
* @
|
|
785
|
+
* Updates the entire card in the card cache and handles any path/parent changes.
|
|
786
|
+
* Also persists changes to content and metadata files.
|
|
787
|
+
* @param card The card with updated information (path, parent, metadata, etc.)
|
|
900
788
|
*/
|
|
901
|
-
async
|
|
902
|
-
const
|
|
903
|
-
const
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
invalidWorkFlow.length === 0 &&
|
|
907
|
-
invalidLabels.length === 0) {
|
|
908
|
-
return '';
|
|
789
|
+
async updateCard(card) {
|
|
790
|
+
const cachedCard = this.cardCache.getCard(card.key);
|
|
791
|
+
const pathChange = cachedCard && cachedCard.path !== card.path;
|
|
792
|
+
if (pathChange) {
|
|
793
|
+
this.changeParent(card, cachedCard.parent);
|
|
909
794
|
}
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
795
|
+
const metadataChanged = cachedCard &&
|
|
796
|
+
JSON.stringify(cachedCard.metadata) !== JSON.stringify(card.metadata);
|
|
797
|
+
if (metadataChanged) {
|
|
798
|
+
await this.saveCardMetadata(card);
|
|
913
799
|
}
|
|
914
|
-
|
|
915
|
-
|
|
800
|
+
const contentChanged = cachedCard && cachedCard.content !== card.content;
|
|
801
|
+
if (contentChanged) {
|
|
802
|
+
await this.saveCardContent(card);
|
|
916
803
|
}
|
|
917
|
-
|
|
918
|
-
|
|
804
|
+
this.cardCache.updateCard(card.key, card);
|
|
805
|
+
if (metadataChanged || contentChanged || pathChange) {
|
|
806
|
+
await this.handleCardChanged(card);
|
|
919
807
|
}
|
|
920
|
-
return errors.join('\n');
|
|
921
808
|
}
|
|
922
809
|
/**
|
|
923
|
-
*
|
|
924
|
-
* @param
|
|
925
|
-
* @
|
|
810
|
+
* Updates a card's metadata.
|
|
811
|
+
* @param card affected card
|
|
812
|
+
* @param changedMetadata changed content for the card
|
|
926
813
|
*/
|
|
927
|
-
async
|
|
928
|
-
|
|
814
|
+
async updateCardMetadata(card, changedMetadata) {
|
|
815
|
+
card.metadata = changedMetadata;
|
|
816
|
+
if (await this.saveCardMetadata(card)) {
|
|
817
|
+
await this.handleCardChanged(card);
|
|
818
|
+
}
|
|
929
819
|
}
|
|
930
820
|
}
|
|
931
821
|
//# sourceMappingURL=project.js.map
|