@cyberismo/data-handler 0.0.12 → 0.0.14
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/command-handler.d.ts +24 -42
- package/dist/command-handler.js +33 -26
- package/dist/command-handler.js.map +1 -1
- package/dist/commands/create.d.ts +3 -3
- package/dist/commands/create.js +7 -22
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.d.ts +12 -11
- package/dist/commands/edit.js +41 -16
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/fetch.js +2 -1
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/import.js +2 -2
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/remove.js +6 -5
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.d.ts +1 -0
- package/dist/commands/rename.js +11 -0
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +4 -0
- package/dist/commands/show.js +6 -12
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/update.d.ts +11 -1
- package/dist/commands/update.js +14 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +2 -1
- package/dist/commands/validate.js +4 -3
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.js +1 -1
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/calculation-engine.js +18 -18
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project.d.ts +2 -1
- package/dist/containers/project.js +5 -1
- package/dist/containers/project.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/command-options.d.ts +81 -0
- package/dist/interfaces/command-options.js +14 -0
- package/dist/interfaces/command-options.js.map +1 -0
- package/dist/interfaces/folder-content-interfaces.d.ts +56 -0
- package/dist/interfaces/folder-content-interfaces.js +47 -0
- package/dist/interfaces/folder-content-interfaces.js.map +1 -0
- package/dist/interfaces/project-interfaces.d.ts +11 -9
- package/dist/interfaces/project-interfaces.js +10 -8
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +37 -10
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/report/index.js +4 -4
- package/dist/macros/report/index.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +71 -0
- package/dist/resources/calculation-resource.js +130 -0
- package/dist/resources/calculation-resource.js.map +1 -0
- package/dist/resources/card-type-resource.js +11 -5
- 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.js +1 -1
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.js +9 -3
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +38 -10
- package/dist/resources/folder-resource.js +108 -12
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +7 -4
- package/dist/resources/graph-model-resource.js +12 -25
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +7 -4
- package/dist/resources/graph-view-resource.js +12 -26
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.js +1 -1
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +14 -10
- package/dist/resources/report-resource.js +41 -45
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +7 -0
- package/dist/resources/resource-object.js +14 -2
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +5 -1
- package/dist/resources/template-resource.js +12 -7
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.js +6 -0
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/constants.js +1 -0
- package/dist/utils/constants.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 +9 -7
- package/src/command-handler.ts +74 -59
- package/src/commands/create.ts +10 -28
- package/src/commands/edit.ts +51 -26
- package/src/commands/fetch.ts +2 -1
- package/src/commands/import.ts +2 -0
- package/src/commands/remove.ts +3 -2
- package/src/commands/rename.ts +20 -0
- package/src/commands/show.ts +5 -13
- package/src/commands/update.ts +20 -2
- package/src/commands/validate.ts +7 -3
- package/src/containers/card-container.ts +1 -1
- package/src/containers/project/calculation-engine.ts +23 -23
- package/src/containers/project.ts +4 -1
- package/src/index.ts +36 -2
- package/src/interfaces/command-options.ts +144 -0
- package/src/interfaces/folder-content-interfaces.ts +81 -0
- package/src/interfaces/project-interfaces.ts +12 -9
- package/src/interfaces/resource-interfaces.ts +51 -12
- package/src/macros/report/index.ts +4 -4
- package/src/resources/calculation-resource.ts +171 -0
- package/src/resources/card-type-resource.ts +12 -6
- package/src/resources/create-defaults.ts +21 -5
- package/src/resources/field-type-resource.ts +1 -1
- package/src/resources/file-resource.ts +9 -3
- package/src/resources/folder-resource.ts +150 -20
- package/src/resources/graph-model-resource.ts +16 -27
- package/src/resources/graph-view-resource.ts +16 -28
- package/src/resources/link-type-resource.ts +1 -1
- package/src/resources/report-resource.ts +60 -62
- package/src/resources/resource-object.ts +30 -7
- package/src/resources/template-resource.ts +12 -7
- package/src/resources/workflow-resource.ts +4 -0
- package/src/utils/constants.ts +1 -0
- package/src/utils/error-utils.ts +62 -0
- package/src/utils/log-utils.ts +0 -68
- package/src/utils/user-preferences.ts +7 -3
|
@@ -1,35 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
Cyberismo
|
|
3
3
|
Copyright © Cyberismo Ltd and contributors 2024
|
|
4
|
+
|
|
4
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
5
6
|
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
-
the Free Software Foundation.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
11
12
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { basename, dirname, join, normalize } from 'node:path';
|
|
15
16
|
import { mkdir, readdir, readFile, rename, rm } from 'node:fs/promises';
|
|
16
17
|
|
|
17
|
-
import {
|
|
18
|
-
|
|
18
|
+
import type { Card, Operation, ResourceName } from './file-resource.js';
|
|
19
|
+
import type {
|
|
20
|
+
ContentUpdateKey,
|
|
21
|
+
ResourceContent,
|
|
22
|
+
UpdateKey,
|
|
23
|
+
} from '../interfaces/resource-interfaces.js';
|
|
19
24
|
import type { ResourceFolderType } from '../interfaces/project-interfaces.js';
|
|
25
|
+
import type { Schema } from 'jsonschema';
|
|
26
|
+
|
|
20
27
|
import {
|
|
21
|
-
type Card,
|
|
22
28
|
DefaultContent,
|
|
23
29
|
FileResource,
|
|
24
|
-
type Operation,
|
|
25
30
|
Project,
|
|
26
31
|
resourceName,
|
|
27
|
-
type ResourceName,
|
|
28
32
|
resourceNameToString,
|
|
29
33
|
sortCards,
|
|
30
34
|
} from './file-resource.js';
|
|
31
|
-
import
|
|
35
|
+
import {
|
|
36
|
+
filename,
|
|
37
|
+
propertyName,
|
|
38
|
+
} from '../interfaces/folder-content-interfaces.js';
|
|
39
|
+
import { readJsonFile } from '../utils/json.js';
|
|
32
40
|
import { VALID_FOLDER_RESOURCE_FILES } from '../utils/constants.js';
|
|
41
|
+
import { writeFileSafe } from '../utils/file-utils.js';
|
|
33
42
|
|
|
34
43
|
export {
|
|
35
44
|
type Card,
|
|
@@ -48,10 +57,23 @@ export {
|
|
|
48
57
|
export class FolderResource extends FileResource {
|
|
49
58
|
protected internalFolder: string = '';
|
|
50
59
|
|
|
60
|
+
// Cache for content files to avoid repeated filesystem operations. Content is stored as string.
|
|
61
|
+
private contentFilesCache = new Map<string, string>();
|
|
62
|
+
|
|
51
63
|
constructor(project: Project, name: ResourceName, type: ResourceFolderType) {
|
|
52
64
|
super(project, name, type);
|
|
53
65
|
}
|
|
54
66
|
|
|
67
|
+
// Clears the content files cache.
|
|
68
|
+
private clearContentCache() {
|
|
69
|
+
this.contentFilesCache.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Type guard to check if a key is a ContentUpdateKey
|
|
73
|
+
private isContentUpdateKey(key: UpdateKey): key is ContentUpdateKey {
|
|
74
|
+
return typeof key === 'object' && key.key === 'content' && 'subKey' in key;
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
/**
|
|
56
78
|
* Creates a new folder type object. Base class writes the object to disk automatically.
|
|
57
79
|
* @param newContent Content for the type.
|
|
@@ -61,6 +83,25 @@ export class FolderResource extends FileResource {
|
|
|
61
83
|
await mkdir(this.internalFolder, { recursive: true });
|
|
62
84
|
}
|
|
63
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Gets content of all files to properties.
|
|
88
|
+
* @returns object with property names as keys and file contents as values.
|
|
89
|
+
*/
|
|
90
|
+
public async contentData(): Promise<Record<string, string | Schema>> {
|
|
91
|
+
const fileNames = await this.showFileNames();
|
|
92
|
+
const content: Record<string, string | Schema> = {};
|
|
93
|
+
|
|
94
|
+
for (const fileName of fileNames) {
|
|
95
|
+
const name = propertyName(fileName);
|
|
96
|
+
if (name) {
|
|
97
|
+
const JSONFile = name === 'schema';
|
|
98
|
+
content[name] = await this.showFile(fileName, JSONFile);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return content;
|
|
103
|
+
}
|
|
104
|
+
|
|
64
105
|
/**
|
|
65
106
|
* Returns content data.
|
|
66
107
|
*/
|
|
@@ -74,6 +115,7 @@ export class FolderResource extends FileResource {
|
|
|
74
115
|
protected async delete() {
|
|
75
116
|
await super.delete();
|
|
76
117
|
await rm(this.internalFolder, { recursive: true, force: true });
|
|
118
|
+
this.clearContentCache();
|
|
77
119
|
}
|
|
78
120
|
|
|
79
121
|
// Get (resource folder) type name
|
|
@@ -81,11 +123,15 @@ export class FolderResource extends FileResource {
|
|
|
81
123
|
return super.getType;
|
|
82
124
|
}
|
|
83
125
|
|
|
126
|
+
// Get logger instance
|
|
84
127
|
protected get logger() {
|
|
85
128
|
return super.getLogger(this.getType);
|
|
86
129
|
}
|
|
87
130
|
|
|
88
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Initialize the resource item.
|
|
133
|
+
*/
|
|
134
|
+
protected initialize() {
|
|
89
135
|
super.initialize();
|
|
90
136
|
|
|
91
137
|
this.internalFolder = join(
|
|
@@ -111,22 +157,69 @@ export class FolderResource extends FileResource {
|
|
|
111
157
|
}
|
|
112
158
|
|
|
113
159
|
/**
|
|
160
|
+
* TODO: to be made protected - no direct access to files
|
|
114
161
|
* Shows the content of a file in the resource.
|
|
115
162
|
* @param fileName Name of the file to show.
|
|
163
|
+
* @param json Content is JSON file.
|
|
116
164
|
* @returns the content of the file.
|
|
117
165
|
*/
|
|
118
|
-
public async showFile(
|
|
166
|
+
public async showFile(
|
|
167
|
+
fileName: string,
|
|
168
|
+
json: boolean = false,
|
|
169
|
+
): Promise<string> {
|
|
170
|
+
// Always first check cache...
|
|
171
|
+
if (this.contentFilesCache.has(fileName)) {
|
|
172
|
+
const cached = this.contentFilesCache.get(fileName)!;
|
|
173
|
+
return json ? JSON.parse(cached) : cached;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ...cache miss, read from filesystem
|
|
119
177
|
const filePath = join(this.internalFolder, fileName);
|
|
120
|
-
|
|
178
|
+
const content = json
|
|
179
|
+
? await readJsonFile(filePath)
|
|
180
|
+
: await readFile(filePath, 'utf8');
|
|
181
|
+
|
|
182
|
+
// Update cache
|
|
183
|
+
const contentStr =
|
|
184
|
+
typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
185
|
+
this.contentFilesCache.set(fileName, contentStr);
|
|
186
|
+
|
|
187
|
+
return json ? content : contentStr;
|
|
121
188
|
}
|
|
122
189
|
|
|
123
190
|
/**
|
|
191
|
+
* TODO: to be made protected - no direct access to files
|
|
124
192
|
* Shows all file names in the resource.
|
|
125
193
|
* @returns all file names in the resource.
|
|
126
194
|
*/
|
|
127
195
|
public async showFileNames(): Promise<string[]> {
|
|
196
|
+
// Always first check cache...
|
|
197
|
+
if (this.contentFilesCache.size > 0) {
|
|
198
|
+
return Array.from(this.contentFilesCache.keys());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ...cache miss, read from filesystem and populate cache
|
|
128
202
|
const files = await readdir(this.internalFolder);
|
|
129
|
-
|
|
203
|
+
const validFiles = files.filter((file) =>
|
|
204
|
+
VALID_FOLDER_RESOURCE_FILES.includes(file),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Update cache by reading all files. Each method call updates specific cache item.
|
|
208
|
+
for (const fileName of validFiles) {
|
|
209
|
+
await this.showFile(fileName);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return validFiles;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Updates content files from a content object.
|
|
217
|
+
* @param contentFiles Object with file names as keys and file contents as values.
|
|
218
|
+
*/
|
|
219
|
+
protected async updateContentFiles(contentFiles: Record<string, string>) {
|
|
220
|
+
for (const [fileName, fileContent] of Object.entries(contentFiles)) {
|
|
221
|
+
await this.updateFile(fileName, fileContent);
|
|
222
|
+
}
|
|
130
223
|
}
|
|
131
224
|
|
|
132
225
|
/**
|
|
@@ -134,7 +227,7 @@ export class FolderResource extends FileResource {
|
|
|
134
227
|
* @param fileName The name of the file to update.
|
|
135
228
|
* @param changedContent The new content for the file.
|
|
136
229
|
*/
|
|
137
|
-
|
|
230
|
+
protected async updateFile(fileName: string, changedContent: string) {
|
|
138
231
|
const filePath = join(this.internalFolder, fileName);
|
|
139
232
|
|
|
140
233
|
// Do not allow updating file in other directories
|
|
@@ -148,22 +241,59 @@ export class FolderResource extends FileResource {
|
|
|
148
241
|
if (basename(normalizedFilePath) !== fileName) {
|
|
149
242
|
throw new Error(`File '${fileName}' is not in the resource`);
|
|
150
243
|
}
|
|
151
|
-
// check if the file is
|
|
244
|
+
// check if the file is allow-listed
|
|
152
245
|
if (!VALID_FOLDER_RESOURCE_FILES.includes(fileName)) {
|
|
153
|
-
throw new Error(`File '${fileName}' is not
|
|
246
|
+
throw new Error(`File '${fileName}' is not allowed`);
|
|
154
247
|
}
|
|
155
248
|
|
|
156
249
|
await writeFileSafe(filePath, changedContent, { flag: 'w' });
|
|
250
|
+
|
|
251
|
+
// Update cache with new content
|
|
252
|
+
this.contentFilesCache.set(fileName, changedContent);
|
|
157
253
|
}
|
|
254
|
+
|
|
158
255
|
/**
|
|
159
256
|
* Updates resource.
|
|
160
257
|
* @param key Key to modify
|
|
161
258
|
* @param op Operation to perform on 'key'
|
|
259
|
+
* @throws if key is unknown.
|
|
162
260
|
*/
|
|
163
|
-
protected async update<Type>(key:
|
|
164
|
-
|
|
261
|
+
protected async update<Type>(key: UpdateKey, op: Operation<Type>) {
|
|
262
|
+
if (this.isContentUpdateKey(key)) {
|
|
263
|
+
const fileName = filename(key.subKey)!;
|
|
264
|
+
const fileContent = super.handleScalar(op) as string;
|
|
265
|
+
await this.updateFile(fileName, fileContent);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const nameChange = key === 'name';
|
|
270
|
+
const existingName = this.content.name;
|
|
271
|
+
await super.update(key, op);
|
|
272
|
+
const content = structuredClone(this.content);
|
|
273
|
+
|
|
274
|
+
if (key === 'name') {
|
|
275
|
+
content.name = super.handleScalar(op) as string;
|
|
276
|
+
} else if (key === 'displayName') {
|
|
277
|
+
content.displayName = super.handleScalar(op) as string;
|
|
278
|
+
} else if (key === 'description') {
|
|
279
|
+
content.description = super.handleScalar(op) as string;
|
|
280
|
+
} else {
|
|
281
|
+
throw new Error(`Unknown property '${key}' for folder resource`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
await super.postUpdate(content, key, op);
|
|
285
|
+
|
|
286
|
+
if (nameChange) {
|
|
287
|
+
await this.onNameChange?.(existingName);
|
|
288
|
+
}
|
|
165
289
|
}
|
|
166
290
|
|
|
291
|
+
/**
|
|
292
|
+
* For handling name changes.
|
|
293
|
+
* @param previousName The previous name before the change
|
|
294
|
+
*/
|
|
295
|
+
protected async onNameChange?(previousName: string): Promise<void>;
|
|
296
|
+
|
|
167
297
|
/**
|
|
168
298
|
* Returns an array of card keys, and/or resource names where this resource is used.
|
|
169
299
|
* @param cards Optional. If defined, only these cards are checked.
|
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
import type {
|
|
31
31
|
GraphModel,
|
|
32
32
|
GraphModelMetadata,
|
|
33
|
+
GraphModelUpdateKey,
|
|
33
34
|
} from '../interfaces/resource-interfaces.js';
|
|
35
|
+
import type { GraphModelContent } from '../interfaces/folder-content-interfaces.js';
|
|
34
36
|
import { writeFileSafe } from '../utils/file-utils.js';
|
|
35
37
|
|
|
36
38
|
/**
|
|
@@ -46,8 +48,11 @@ export class GraphModelResource extends FolderResource {
|
|
|
46
48
|
this.initialize();
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Handle name changes for graph models
|
|
53
|
+
* @param existingName The previous name before the change
|
|
54
|
+
*/
|
|
55
|
+
protected async onNameChange(existingName: string): Promise<void> {
|
|
51
56
|
await Promise.all([
|
|
52
57
|
super.updateHandleBars(existingName, this.content.name, [
|
|
53
58
|
await this.calculationFile(),
|
|
@@ -122,7 +127,7 @@ export class GraphModelResource extends FolderResource {
|
|
|
122
127
|
public async rename(newName: ResourceName) {
|
|
123
128
|
const existingName = this.content.name;
|
|
124
129
|
await super.rename(newName);
|
|
125
|
-
return this.
|
|
130
|
+
return this.onNameChange(existingName);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
/**
|
|
@@ -130,11 +135,10 @@ export class GraphModelResource extends FolderResource {
|
|
|
130
135
|
* @returns graph model metadata.
|
|
131
136
|
*/
|
|
132
137
|
public async show(): Promise<GraphModel> {
|
|
133
|
-
const showOnlyFileName = true;
|
|
134
138
|
const baseData = (await super.show()) as GraphModelMetadata;
|
|
135
139
|
return {
|
|
136
140
|
...baseData,
|
|
137
|
-
|
|
141
|
+
content: (await super.contentData()) as GraphModelContent,
|
|
138
142
|
};
|
|
139
143
|
}
|
|
140
144
|
|
|
@@ -142,32 +146,17 @@ export class GraphModelResource extends FolderResource {
|
|
|
142
146
|
* Updates graph model resource.
|
|
143
147
|
* @param key Key to modify
|
|
144
148
|
* @param op Operation to perform on 'key'
|
|
145
|
-
* @throws if key is unknown.
|
|
146
149
|
*/
|
|
147
|
-
public async update<Type>(key:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
await super.update(key, op);
|
|
152
|
-
|
|
153
|
-
const content = structuredClone(this.content) as GraphModel;
|
|
154
|
-
|
|
155
|
-
if (key === 'name') {
|
|
156
|
-
content.name = super.handleScalar(op) as string;
|
|
157
|
-
} else if (key === 'displayName') {
|
|
158
|
-
content.displayName = super.handleScalar(op) as string;
|
|
159
|
-
} else if (key === 'description') {
|
|
160
|
-
content.description = super.handleScalar(op) as string;
|
|
161
|
-
} else if (key === 'category') {
|
|
150
|
+
public async update<Type>(key: GraphModelUpdateKey, op: Operation<Type>) {
|
|
151
|
+
if (key === 'category') {
|
|
152
|
+
const content = structuredClone(this.content) as GraphModelMetadata;
|
|
162
153
|
content.category = super.handleScalar(op) as string;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
await super.postUpdate(content, key, op);
|
|
166
154
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
await this.handleNameChange(existingName);
|
|
155
|
+
await super.postUpdate(content, key, op);
|
|
156
|
+
return;
|
|
170
157
|
}
|
|
158
|
+
|
|
159
|
+
await super.update(key, op);
|
|
171
160
|
}
|
|
172
161
|
|
|
173
162
|
/**
|
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
import type {
|
|
31
31
|
GraphView,
|
|
32
32
|
GraphViewMetadata,
|
|
33
|
+
GraphViewUpdateKey,
|
|
33
34
|
} from '../interfaces/resource-interfaces.js';
|
|
35
|
+
import type { GraphViewContent } from '../interfaces/folder-content-interfaces.js';
|
|
34
36
|
|
|
35
37
|
import { getStaticDirectoryPath } from '@cyberismo/assets';
|
|
36
38
|
import { copyDir } from '../utils/file-utils.js';
|
|
@@ -48,15 +50,17 @@ export class GraphViewResource extends FolderResource {
|
|
|
48
50
|
this.initialize();
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Handle name changes for graph views
|
|
55
|
+
* @param existingName The previous name before the change
|
|
56
|
+
*/
|
|
57
|
+
protected async onNameChange(existingName: string): Promise<void> {
|
|
53
58
|
await Promise.all([
|
|
54
59
|
super.updateHandleBars(existingName, this.content.name, [
|
|
55
60
|
await this.handleBarFile(),
|
|
56
61
|
]),
|
|
57
62
|
super.updateCalculations(existingName, this.content.name),
|
|
58
63
|
]);
|
|
59
|
-
// Finally, write updated content.
|
|
60
64
|
await this.write();
|
|
61
65
|
}
|
|
62
66
|
|
|
@@ -118,7 +122,7 @@ export class GraphViewResource extends FolderResource {
|
|
|
118
122
|
public async rename(newName: ResourceName) {
|
|
119
123
|
const existingName = this.content.name;
|
|
120
124
|
await super.rename(newName);
|
|
121
|
-
return this.
|
|
125
|
+
return this.onNameChange(existingName);
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
/**
|
|
@@ -126,11 +130,10 @@ export class GraphViewResource extends FolderResource {
|
|
|
126
130
|
* @returns graph view metadata.
|
|
127
131
|
*/
|
|
128
132
|
public async show(): Promise<GraphView> {
|
|
129
|
-
const showOnlyFileName = true;
|
|
130
133
|
const baseData = (await super.show()) as GraphViewMetadata;
|
|
131
134
|
return {
|
|
132
135
|
...baseData,
|
|
133
|
-
|
|
136
|
+
content: (await super.contentData()) as GraphViewContent,
|
|
134
137
|
};
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -138,32 +141,17 @@ export class GraphViewResource extends FolderResource {
|
|
|
138
141
|
* Updates graph view resource.
|
|
139
142
|
* @param key Key to modify
|
|
140
143
|
* @param op Operation to perform on 'key'
|
|
141
|
-
* @throws if key is unknown.
|
|
142
144
|
*/
|
|
143
|
-
public async update<Type>(key:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await super.update(key, op);
|
|
148
|
-
|
|
149
|
-
const content = structuredClone(this.content) as GraphView;
|
|
150
|
-
|
|
151
|
-
if (key === 'name') {
|
|
152
|
-
content.name = super.handleScalar(op) as string;
|
|
153
|
-
} else if (key === 'displayName') {
|
|
154
|
-
content.displayName = super.handleScalar(op) as string;
|
|
155
|
-
} else if (key === 'description') {
|
|
156
|
-
content.description = super.handleScalar(op) as string;
|
|
157
|
-
} else if (key === 'category') {
|
|
145
|
+
public async update<Type>(key: GraphViewUpdateKey, op: Operation<Type>) {
|
|
146
|
+
if (key === 'category') {
|
|
147
|
+
const content = structuredClone(this.content) as GraphViewMetadata;
|
|
158
148
|
content.category = super.handleScalar(op) as string;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
await super.postUpdate(content, key, op);
|
|
162
149
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await this.handleNameChange(existingName);
|
|
150
|
+
await super.postUpdate(content, key, op);
|
|
151
|
+
return;
|
|
166
152
|
}
|
|
153
|
+
|
|
154
|
+
await super.update(key, op);
|
|
167
155
|
}
|
|
168
156
|
|
|
169
157
|
/**
|
|
@@ -117,7 +117,7 @@ export class LinkTypeResource extends FileResource {
|
|
|
117
117
|
|
|
118
118
|
await super.update(key, op);
|
|
119
119
|
|
|
120
|
-
const content = this.content as LinkType;
|
|
120
|
+
const content = structuredClone(this.content) as LinkType;
|
|
121
121
|
if (key === 'name') {
|
|
122
122
|
content.name = super.handleScalar(op) as string;
|
|
123
123
|
} else if (key === 'destinationCardTypes') {
|
|
@@ -1,43 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
Cyberismo
|
|
3
3
|
Copyright © Cyberismo Ltd and contributors 2024
|
|
4
|
+
|
|
4
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
5
6
|
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
-
the Free Software Foundation.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
11
12
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
|
-
import { readdir
|
|
15
|
+
import { readdir } from 'node:fs/promises';
|
|
15
16
|
import { readFileSync } from 'node:fs';
|
|
16
17
|
import { extname, join } from 'node:path';
|
|
17
18
|
|
|
18
19
|
import { copyDir } from '../utils/file-utils.js';
|
|
19
|
-
import type {
|
|
20
|
-
Card,
|
|
21
|
-
Operation,
|
|
22
|
-
Project,
|
|
23
|
-
ResourceName,
|
|
24
|
-
} from './folder-resource.js';
|
|
25
20
|
import {
|
|
26
21
|
DefaultContent,
|
|
27
22
|
FolderResource,
|
|
28
23
|
resourceNameToString,
|
|
29
24
|
sortCards,
|
|
30
25
|
} from './folder-resource.js';
|
|
26
|
+
import { getStaticDirectoryPath } from '@cyberismo/assets';
|
|
27
|
+
import { filename } from '../interfaces/folder-content-interfaces.js';
|
|
28
|
+
import { Validate } from '../commands/validate.js';
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
Card,
|
|
32
|
+
Operation,
|
|
33
|
+
Project,
|
|
34
|
+
ResourceName,
|
|
35
|
+
} from './folder-resource.js';
|
|
31
36
|
import type {
|
|
32
37
|
Report,
|
|
33
38
|
ReportMetadata,
|
|
39
|
+
ReportUpdateKey,
|
|
34
40
|
} from '../interfaces/resource-interfaces.js';
|
|
41
|
+
import type { ReportContent } from '../interfaces/folder-content-interfaces.js';
|
|
35
42
|
import type { Schema } from 'jsonschema';
|
|
36
|
-
import { getStaticDirectoryPath } from '@cyberismo/assets';
|
|
37
|
-
import { Validate } from '../commands/validate.js';
|
|
38
43
|
|
|
39
|
-
const CARD_CONTENT_HANDLEBAR_FILE = 'index.adoc.hbs';
|
|
40
|
-
const QUERY_HANDLEBAR_FILE = 'query.lp.hbs';
|
|
41
44
|
const REPORT_SCHEMA_FILE = 'parameterSchema.json';
|
|
42
45
|
const PARAMETER_SCHEMA_ID = 'jsonSchema';
|
|
43
46
|
|
|
@@ -67,8 +70,21 @@ export class ReportResource extends FolderResource {
|
|
|
67
70
|
'defaultReport',
|
|
68
71
|
);
|
|
69
72
|
|
|
70
|
-
//
|
|
71
|
-
private
|
|
73
|
+
// Try to read schema file content
|
|
74
|
+
private readSchemaFile(path: string) {
|
|
75
|
+
try {
|
|
76
|
+
const schema = readFileSync(path);
|
|
77
|
+
return JSON.parse(schema.toString());
|
|
78
|
+
} catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Handle name changes for reports
|
|
85
|
+
* @param existingName The previous name before the change
|
|
86
|
+
*/
|
|
87
|
+
protected async onNameChange(existingName: string): Promise<void> {
|
|
72
88
|
await Promise.all([
|
|
73
89
|
super.updateHandleBars(
|
|
74
90
|
existingName,
|
|
@@ -81,16 +97,6 @@ export class ReportResource extends FolderResource {
|
|
|
81
97
|
await this.write();
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
// Try to read schema file content
|
|
85
|
-
private readSchemaFile(path: string) {
|
|
86
|
-
try {
|
|
87
|
-
const schema = readFileSync(path);
|
|
88
|
-
return JSON.parse(schema.toString());
|
|
89
|
-
} catch {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
100
|
/**
|
|
95
101
|
* Sets new metadata into the report object.
|
|
96
102
|
* @param newContent metadata content for the report.
|
|
@@ -143,7 +149,7 @@ export class ReportResource extends FolderResource {
|
|
|
143
149
|
public async rename(newName: ResourceName) {
|
|
144
150
|
const existingName = this.content.name;
|
|
145
151
|
await super.rename(newName);
|
|
146
|
-
return this.
|
|
152
|
+
return this.onNameChange(existingName);
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
/**
|
|
@@ -151,19 +157,16 @@ export class ReportResource extends FolderResource {
|
|
|
151
157
|
* @returns report metadata.
|
|
152
158
|
*/
|
|
153
159
|
public async show(): Promise<Report> {
|
|
154
|
-
const
|
|
160
|
+
const baseData = (await super.show()) as ReportMetadata;
|
|
161
|
+
const fileContents = await super.contentData();
|
|
162
|
+
const content: ReportContent = {
|
|
163
|
+
contentTemplate: fileContents.contentTemplate as string,
|
|
164
|
+
queryTemplate: fileContents.queryTemplate as string,
|
|
165
|
+
schema: fileContents.schema ? (fileContents.schema as Schema) : undefined,
|
|
166
|
+
};
|
|
155
167
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
description: reportMetadata.description,
|
|
159
|
-
metadata: reportMetadata,
|
|
160
|
-
contentTemplate: (
|
|
161
|
-
await readFile(join(this.internalFolder, CARD_CONTENT_HANDLEBAR_FILE))
|
|
162
|
-
).toString(),
|
|
163
|
-
queryTemplate: (
|
|
164
|
-
await readFile(join(this.internalFolder, QUERY_HANDLEBAR_FILE))
|
|
165
|
-
).toString(),
|
|
166
|
-
schema: this.reportSchema,
|
|
168
|
+
...baseData,
|
|
169
|
+
content: content,
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
172
|
|
|
@@ -171,32 +174,27 @@ export class ReportResource extends FolderResource {
|
|
|
171
174
|
* Updates report resource.
|
|
172
175
|
* @param key Key to modify
|
|
173
176
|
* @param op Operation to perform on 'key'
|
|
174
|
-
* @throws if key is unknown.
|
|
175
177
|
*/
|
|
176
|
-
public async update<Type>(key:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
content.name = super.handleScalar(op) as string;
|
|
186
|
-
} else if (key === 'displayName') {
|
|
187
|
-
content.displayName = super.handleScalar(op) as string;
|
|
188
|
-
} else if (key === 'description') {
|
|
189
|
-
content.description = super.handleScalar(op) as string;
|
|
190
|
-
} else if (key === 'category') {
|
|
191
|
-
content.category = super.handleScalar(op) as string;
|
|
178
|
+
public async update<Type>(key: ReportUpdateKey, op: Operation<Type>) {
|
|
179
|
+
if (
|
|
180
|
+
typeof key === 'object' &&
|
|
181
|
+
key.key === 'content' &&
|
|
182
|
+
key.subKey === 'schema'
|
|
183
|
+
) {
|
|
184
|
+
const fileContent = JSON.stringify(super.handleScalar(op), null, 2);
|
|
185
|
+
await this.updateFile(filename(key.subKey)!, fileContent);
|
|
186
|
+
return;
|
|
192
187
|
}
|
|
193
188
|
|
|
194
|
-
|
|
189
|
+
if (key === 'category') {
|
|
190
|
+
const content = structuredClone(this.content) as ReportMetadata;
|
|
191
|
+
content.category = super.handleScalar(op) as string;
|
|
195
192
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
await this.handleNameChange(existingName);
|
|
193
|
+
await super.postUpdate(content, key, op);
|
|
194
|
+
return;
|
|
199
195
|
}
|
|
196
|
+
|
|
197
|
+
await super.update(key, op);
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
/**
|