@cyberismo/data-handler 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card-metadata-updater.js +1 -3
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.js +10 -16
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +1 -1
- package/dist/command-manager.js +4 -3
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.js +13 -59
- 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.js +4 -17
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.js +3 -5
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/move.d.ts +1 -2
- package/dist/commands/move.js +108 -146
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.js +9 -44
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.js +2 -7
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +7 -25
- package/dist/commands/show.js +38 -102
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.js +27 -30
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.d.ts +5 -3
- package/dist/commands/update.js +19 -5
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +3 -3
- package/dist/commands/validate.js +19 -26
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.d.ts +87 -24
- package/dist/containers/card-container.js +183 -279
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/calculation-engine.d.ts +6 -0
- package/dist/containers/project/calculation-engine.js +19 -12
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project/card-cache.d.ts +146 -0
- package/dist/containers/project/card-cache.js +411 -0
- package/dist/containers/project/card-cache.js.map +1 -0
- package/dist/containers/project/resource-collector.d.ts +24 -1
- package/dist/containers/project/resource-collector.js +8 -1
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +117 -83
- package/dist/containers/project.js +418 -252
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +15 -31
- package/dist/containers/template.js +97 -104
- package/dist/containers/template.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/folder-content-interfaces.d.ts +2 -1
- 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 +5 -1
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +11 -21
- package/dist/interfaces/resource-interfaces.js +3 -0
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/common.d.ts +10 -10
- package/dist/macros/createCards/index.d.ts +0 -13
- package/dist/macros/createCards/index.js.map +1 -1
- package/dist/macros/createCards/types.d.ts +44 -0
- package/dist/macros/createCards/types.js +15 -0
- package/dist/macros/createCards/types.js.map +1 -0
- package/dist/macros/graph/index.d.ts +2 -6
- package/dist/macros/graph/index.js +2 -2
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/graph/types.d.ts +23 -0
- package/dist/macros/graph/types.js +15 -0
- package/dist/macros/graph/types.js.map +1 -0
- package/dist/macros/image/index.d.ts +8 -16
- package/dist/macros/image/index.js +36 -33
- package/dist/macros/image/index.js.map +1 -1
- package/dist/macros/image/types.d.ts +38 -0
- package/dist/macros/image/types.js +15 -0
- package/dist/macros/image/types.js.map +1 -0
- package/dist/macros/include/index.d.ts +1 -6
- package/dist/macros/include/index.js +4 -7
- package/dist/macros/include/index.js.map +1 -1
- package/dist/macros/include/types.d.ts +31 -0
- package/dist/macros/include/types.js +15 -0
- package/dist/macros/include/types.js.map +1 -0
- package/dist/macros/percentage/index.d.ts +0 -6
- package/dist/macros/percentage/index.js.map +1 -1
- package/dist/macros/percentage/types.d.ts +31 -0
- package/dist/macros/percentage/types.js +15 -0
- package/dist/macros/percentage/types.js.map +1 -0
- package/dist/macros/report/index.d.ts +0 -3
- package/dist/macros/report/index.js.map +1 -1
- package/dist/macros/report/types.d.ts +19 -0
- package/dist/macros/report/types.js +15 -0
- package/dist/macros/report/types.js.map +1 -0
- package/dist/macros/scoreCard/index.d.ts +0 -6
- package/dist/macros/scoreCard/index.js.map +1 -1
- package/dist/macros/scoreCard/types.d.ts +31 -0
- package/dist/macros/scoreCard/types.js +15 -0
- package/dist/macros/scoreCard/types.js.map +1 -0
- package/dist/macros/types.d.ts +25 -0
- package/dist/macros/types.js +2 -0
- package/dist/macros/types.js.map +1 -0
- package/dist/macros/vega/index.d.ts +0 -4
- package/dist/macros/vega/index.js.map +1 -1
- package/dist/macros/vega/types.d.ts +20 -0
- package/dist/macros/vega/types.js +2 -0
- package/dist/macros/vega/types.js.map +1 -0
- package/dist/macros/vegalite/index.d.ts +0 -4
- package/dist/macros/vegalite/index.js.map +1 -1
- package/dist/macros/vegalite/types.d.ts +20 -0
- package/dist/macros/vegalite/types.js +15 -0
- package/dist/macros/vegalite/types.js.map +1 -0
- package/dist/macros/xref/index.d.ts +0 -3
- package/dist/macros/xref/index.js +5 -14
- package/dist/macros/xref/index.js.map +1 -1
- package/dist/macros/xref/types.d.ts +19 -0
- package/dist/macros/xref/types.js +15 -0
- package/dist/macros/xref/types.js.map +1 -0
- package/dist/module-manager.js +4 -4
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +4 -32
- package/dist/resources/calculation-resource.js +0 -55
- package/dist/resources/calculation-resource.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +4 -21
- package/dist/resources/card-type-resource.js +13 -44
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +4 -21
- package/dist/resources/field-type-resource.js +14 -38
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +12 -29
- package/dist/resources/file-resource.js +19 -293
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +31 -50
- package/dist/resources/folder-resource.js +68 -96
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +5 -33
- package/dist/resources/graph-model-resource.js +8 -61
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +5 -28
- package/dist/resources/graph-view-resource.js +6 -45
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +4 -21
- package/dist/resources/link-type-resource.js +6 -31
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +5 -17
- package/dist/resources/report-resource.js +6 -44
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +58 -23
- package/dist/resources/resource-object.js +293 -24
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +4 -15
- package/dist/resources/template-resource.js +10 -25
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +4 -23
- package/dist/resources/workflow-resource.js +12 -38
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/card-utils.d.ts +69 -19
- package/dist/utils/card-utils.js +179 -30
- package/dist/utils/card-utils.js.map +1 -1
- package/dist/utils/clingo-facts.js +11 -3
- package/dist/utils/clingo-facts.js.map +1 -1
- package/dist/utils/clingo-parser.js +1 -1
- package/dist/utils/clingo-parser.js.map +1 -1
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.js +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/package.json +5 -5
- package/src/card-metadata-updater.ts +3 -5
- package/src/command-handler.ts +11 -18
- package/src/command-manager.ts +4 -3
- package/src/commands/create.ts +17 -83
- package/src/commands/edit.ts +16 -132
- package/src/commands/export.ts +8 -29
- package/src/commands/import.ts +4 -6
- package/src/commands/move.ts +144 -179
- package/src/commands/remove.ts +9 -52
- package/src/commands/rename.ts +2 -7
- package/src/commands/show.ts +50 -143
- package/src/commands/transition.ts +30 -33
- package/src/commands/update.ts +27 -9
- package/src/commands/validate.ts +21 -36
- package/src/containers/card-container.ts +200 -360
- package/src/containers/project/calculation-engine.ts +21 -13
- package/src/containers/project/card-cache.ts +497 -0
- package/src/containers/project/resource-collector.ts +9 -1
- package/src/containers/project.ts +529 -327
- package/src/containers/template.ts +109 -127
- package/src/index.ts +1 -0
- package/src/interfaces/folder-content-interfaces.ts +7 -1
- package/src/interfaces/macros.ts +2 -0
- package/src/interfaces/project-interfaces.ts +7 -1
- package/src/interfaces/resource-interfaces.ts +12 -24
- package/src/macros/createCards/index.ts +1 -12
- package/src/macros/createCards/types.ts +46 -0
- package/src/macros/graph/index.ts +3 -7
- package/src/macros/graph/types.ts +24 -0
- package/src/macros/image/index.ts +50 -61
- package/src/macros/image/types.ts +39 -0
- package/src/macros/include/index.ts +6 -15
- package/src/macros/include/types.ts +32 -0
- package/src/macros/percentage/index.ts +1 -7
- package/src/macros/percentage/types.ts +32 -0
- package/src/macros/report/index.ts +1 -4
- package/src/macros/report/types.ts +20 -0
- package/src/macros/scoreCard/index.ts +1 -7
- package/src/macros/scoreCard/types.ts +32 -0
- package/src/macros/types.ts +48 -0
- package/src/macros/vega/index.ts +1 -4
- package/src/macros/vega/types.ts +21 -0
- package/src/macros/vegalite/index.ts +1 -4
- package/src/macros/vegalite/types.ts +22 -0
- package/src/macros/xref/index.ts +6 -20
- package/src/macros/xref/types.ts +20 -0
- package/src/module-manager.ts +5 -5
- package/src/project-settings.ts +1 -1
- package/src/resources/calculation-resource.ts +6 -76
- package/src/resources/card-type-resource.ts +24 -59
- package/src/resources/field-type-resource.ts +22 -51
- package/src/resources/file-resource.ts +27 -409
- package/src/resources/folder-resource.ts +98 -124
- package/src/resources/graph-model-resource.ts +17 -74
- package/src/resources/graph-view-resource.ts +14 -54
- package/src/resources/link-type-resource.ts +13 -40
- package/src/resources/report-resource.ts +17 -57
- package/src/resources/resource-object.ts +435 -32
- package/src/resources/template-resource.ts +16 -29
- package/src/resources/workflow-resource.ts +26 -50
- package/src/utils/card-utils.ts +217 -31
- package/src/utils/clingo-facts.ts +13 -3
- package/src/utils/clingo-parser.ts +1 -1
- package/src/utils/constants.ts +6 -0
- package/src/utils/csv.ts +1 -1
|
@@ -13,21 +13,38 @@
|
|
|
13
13
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
14
14
|
|
|
15
15
|
// node
|
|
16
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
17
|
-
import { basename, join } from 'node:path';
|
|
16
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
17
|
+
import { basename, join, sep } from 'node:path';
|
|
18
18
|
|
|
19
19
|
import { hasCode } from '../utils/error-utils.js';
|
|
20
20
|
|
|
21
21
|
import { ArrayHandler } from './array-handler.js';
|
|
22
22
|
import type {
|
|
23
23
|
Card,
|
|
24
|
+
Resource,
|
|
24
25
|
ResourceFolderType,
|
|
25
26
|
} from '../interfaces/project-interfaces.js';
|
|
26
27
|
import type { Logger } from 'pino';
|
|
27
|
-
import {
|
|
28
|
-
import
|
|
29
|
-
import type {
|
|
28
|
+
import type { Project } from '../containers/project.js';
|
|
29
|
+
import { ResourcesFrom } from '../containers/project/resource-collector.js';
|
|
30
|
+
import type {
|
|
31
|
+
ResourceBaseMetadata,
|
|
32
|
+
UpdateKey,
|
|
33
|
+
} from '../interfaces/resource-interfaces.js';
|
|
34
|
+
import type { Validate } from '../commands/validate.js';
|
|
35
|
+
import {
|
|
36
|
+
resourceName,
|
|
37
|
+
resourceNameToPath,
|
|
38
|
+
resourceNameToString,
|
|
39
|
+
type ResourceName,
|
|
40
|
+
} from '../utils/resource-utils.js';
|
|
30
41
|
import { getChildLogger } from '../utils/log-utils.js';
|
|
42
|
+
import { deleteFile, pathExists } from '../utils/file-utils.js';
|
|
43
|
+
import {
|
|
44
|
+
readJsonFile,
|
|
45
|
+
readJsonFileSync,
|
|
46
|
+
writeJsonFile,
|
|
47
|
+
} from '../utils/json.js';
|
|
31
48
|
|
|
32
49
|
// Possible operations to perform when doing "update"
|
|
33
50
|
export type UpdateOperations = 'add' | 'change' | 'rank' | 'remove';
|
|
@@ -80,18 +97,29 @@ export type OperationMap<T> = {
|
|
|
80
97
|
// Given an operation name, get the corresponding operation type
|
|
81
98
|
export type OperationFor<T, N extends UpdateOperations> = OperationMap<T>[N];
|
|
82
99
|
|
|
100
|
+
// T, but U is in the content field
|
|
101
|
+
export type ShowReturnType<T extends ResourceBaseMetadata, U = never> = Omit<
|
|
102
|
+
T,
|
|
103
|
+
'content'
|
|
104
|
+
> & {
|
|
105
|
+
[K in 'content' as [U] extends [never] ? never : K]: U;
|
|
106
|
+
};
|
|
107
|
+
|
|
83
108
|
/**
|
|
84
109
|
* Abstract class for resources.
|
|
85
110
|
*/
|
|
86
|
-
export abstract class AbstractResource
|
|
111
|
+
export abstract class AbstractResource<
|
|
112
|
+
T extends ResourceBaseMetadata,
|
|
113
|
+
U = never, // determines type returned by show()
|
|
114
|
+
> {
|
|
87
115
|
protected abstract calculate(): Promise<void>; // update resource specific calculations
|
|
88
|
-
protected abstract create(content?:
|
|
116
|
+
protected abstract create(content?: T): Promise<void>; // create a new with the content (memory)
|
|
89
117
|
protected abstract delete(): Promise<void>; // delete from disk
|
|
90
118
|
protected abstract read(): Promise<void>; // read content from disk (replaces existing content, if any)
|
|
91
119
|
protected abstract rename(newName: ResourceName): Promise<void>; // change name of the resource and filename; same as update('name', ...)
|
|
92
|
-
protected abstract show(): Promise<
|
|
93
|
-
protected abstract update<Type>(
|
|
94
|
-
|
|
120
|
+
protected abstract show(): Promise<ShowReturnType<T, U>>; // return the content as JSON
|
|
121
|
+
protected abstract update<Type, K extends string>(
|
|
122
|
+
updateKey: UpdateKey<K>,
|
|
95
123
|
operation: Operation<Type>,
|
|
96
124
|
): Promise<void>; // change one key of resource
|
|
97
125
|
protected abstract usage(cards?: Card[]): Promise<string[]>; // list of card keys or resource names where this resource is used in
|
|
@@ -102,47 +130,125 @@ export abstract class AbstractResource {
|
|
|
102
130
|
protected abstract getLogger(loggerName: string): Logger;
|
|
103
131
|
}
|
|
104
132
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
133
|
+
type ValidateInstance = InstanceType<typeof Validate>;
|
|
134
|
+
|
|
135
|
+
export abstract class ResourceObject<
|
|
136
|
+
T extends ResourceBaseMetadata,
|
|
137
|
+
U,
|
|
138
|
+
> extends AbstractResource<T, U> {
|
|
139
|
+
// TODO: Remove when INTDEV-1048 is implemented, since caching is done at object level
|
|
140
|
+
private cache: Map<string, JSON>;
|
|
141
|
+
private static validateInstancePromise?: Promise<ValidateInstance>;
|
|
142
|
+
|
|
143
|
+
protected content: T;
|
|
109
144
|
protected moduleResource: boolean;
|
|
110
145
|
protected contentSchema: JSON = {} as JSON;
|
|
111
146
|
protected contentSchemaId: string = '';
|
|
112
147
|
protected type: ResourceFolderType = '' as ResourceFolderType;
|
|
113
148
|
protected resourceFolder: string = '';
|
|
149
|
+
protected logger: Logger;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Path to the resource metadata file (the .json file).
|
|
153
|
+
*/
|
|
154
|
+
public fileName: string = '';
|
|
114
155
|
|
|
115
156
|
constructor(
|
|
116
157
|
protected project: Project,
|
|
117
158
|
protected resourceName: ResourceName,
|
|
159
|
+
type: ResourceFolderType,
|
|
118
160
|
) {
|
|
119
161
|
super();
|
|
120
162
|
this.moduleResource =
|
|
121
163
|
this.resourceName.prefix !== this.project.projectPrefix;
|
|
164
|
+
this.cache = this.project.resourceCache;
|
|
165
|
+
this.type = type;
|
|
166
|
+
this.logger = this.getLogger(this.getType);
|
|
167
|
+
this.content = { name: '' } as T; // not found if name is empty
|
|
122
168
|
}
|
|
123
169
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
170
|
+
private static async getValidate(): Promise<ValidateInstance> {
|
|
171
|
+
// a bit hacky solution to avoid circular dependencies
|
|
172
|
+
if (!this.validateInstancePromise) {
|
|
173
|
+
this.validateInstancePromise = import('../commands/validate.js').then(
|
|
174
|
+
({ Validate }) => Validate.getInstance(),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return this.validateInstancePromise;
|
|
131
178
|
}
|
|
132
|
-
|
|
179
|
+
|
|
180
|
+
private resourceObjectToResource(): Resource {
|
|
181
|
+
return {
|
|
182
|
+
name: this.data ? this.data.name : '',
|
|
183
|
+
path: this.fileName.substring(0, this.fileName.lastIndexOf(sep)),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Type of resource.
|
|
188
|
+
private resourceType(): ResourceFolderType {
|
|
133
189
|
return this.type;
|
|
134
190
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
191
|
+
|
|
192
|
+
private toCache() {
|
|
193
|
+
this.cache.set(
|
|
194
|
+
resourceNameToString(this.resourceName),
|
|
195
|
+
this.content as unknown as JSON,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Checks if resource exists
|
|
201
|
+
* @throws if resource does not exist
|
|
202
|
+
*/
|
|
203
|
+
protected assertResourceExists() {
|
|
204
|
+
if (!pathExists(this.fileName)) {
|
|
205
|
+
const resourceType = `${this.type[0].toUpperCase()}${this.type.slice(1, this.type.length - 1)}`;
|
|
206
|
+
const name = resourceNameToString(this.resourceName);
|
|
207
|
+
throw new Error(
|
|
208
|
+
`${resourceType} '${name}' does not exist in the project`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
protected async calculate() {}
|
|
214
|
+
|
|
215
|
+
// Calculations that use this resource.
|
|
216
|
+
protected async calculations(): Promise<string[]> {
|
|
217
|
+
const references: string[] = [];
|
|
218
|
+
const resourceName = resourceNameToString(this.resourceName);
|
|
219
|
+
for (const calculation of await this.project.calculations(
|
|
220
|
+
ResourcesFrom.all,
|
|
221
|
+
)) {
|
|
222
|
+
const fileNameWithExtension = calculation.name.endsWith('.lp')
|
|
223
|
+
? calculation.name
|
|
224
|
+
: calculation.name + '.lp';
|
|
225
|
+
const filename = join(calculation.path, basename(fileNameWithExtension));
|
|
226
|
+
try {
|
|
227
|
+
const content = await readFile(filename, 'utf-8');
|
|
228
|
+
if (content.includes(resourceName)) {
|
|
229
|
+
references.push(calculation.name);
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
// Skip files that don't exist (they may have been renamed or deleted)
|
|
233
|
+
if (hasCode(error) && error.code === 'ENOENT') {
|
|
234
|
+
this.logger.warn(`Skipping non-existent file: ${filename}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Failed to process file ${filename}: ${(error as Error).message}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return references;
|
|
139
243
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
244
|
+
|
|
245
|
+
// Cards from project.
|
|
246
|
+
protected cards(): Card[] {
|
|
247
|
+
return [
|
|
248
|
+
...this.project.cards(undefined),
|
|
249
|
+
...this.project.allTemplateCards(),
|
|
250
|
+
];
|
|
143
251
|
}
|
|
144
|
-
protected async validate(_content?: object) {}
|
|
145
|
-
protected async write() {}
|
|
146
252
|
|
|
147
253
|
/**
|
|
148
254
|
* Returns .schema content file.
|
|
@@ -158,6 +264,59 @@ export class ResourceObject extends AbstractResource {
|
|
|
158
264
|
] as unknown as JSON;
|
|
159
265
|
}
|
|
160
266
|
|
|
267
|
+
// Creates resource.
|
|
268
|
+
protected async create(newContent?: T) {
|
|
269
|
+
if (pathExists(this.fileName)) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Resource '${this.resourceName.identifier}' already exists in the project`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.resourceFolder === '') {
|
|
276
|
+
this.resourceName = resourceName(
|
|
277
|
+
`${this.project.projectPrefix}/${this.type}/${this.resourceName.identifier}`,
|
|
278
|
+
);
|
|
279
|
+
this.resourceFolder = this.project.paths.resourcePath(
|
|
280
|
+
this.resourceName.type as ResourceFolderType,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const validator = await ResourceObject.getValidate();
|
|
285
|
+
const validName = validator.validResourceName(
|
|
286
|
+
this.resourceType(),
|
|
287
|
+
resourceNameToString(this.resourceName),
|
|
288
|
+
await this.project.projectPrefixes(),
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
let validContent = {} as T;
|
|
292
|
+
if (newContent) {
|
|
293
|
+
validContent = newContent;
|
|
294
|
+
validContent.name = validName;
|
|
295
|
+
} else {
|
|
296
|
+
validContent.description = '';
|
|
297
|
+
validContent.displayName = '';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.content = validContent;
|
|
301
|
+
await this.write();
|
|
302
|
+
|
|
303
|
+
// Notify project & collector
|
|
304
|
+
this.project.addResource(
|
|
305
|
+
this.resourceObjectToResource(),
|
|
306
|
+
this.content as unknown as JSON,
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
protected getLogger(loggerName: string): Logger {
|
|
311
|
+
return getChildLogger({
|
|
312
|
+
module: loggerName,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
protected get getType(): string {
|
|
317
|
+
return this.type;
|
|
318
|
+
}
|
|
319
|
+
|
|
161
320
|
/**
|
|
162
321
|
* Handles operation to an array.
|
|
163
322
|
* @param operation Operation to perform on array.
|
|
@@ -197,7 +356,142 @@ export class ResourceObject extends AbstractResource {
|
|
|
197
356
|
) {
|
|
198
357
|
throw new Error(`Cannot do operation ${operation.name} on scalar value`);
|
|
199
358
|
}
|
|
200
|
-
return
|
|
359
|
+
return operation.to;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Initialize the resource.
|
|
363
|
+
protected initialize() {
|
|
364
|
+
if (this.resourceName.type === '') {
|
|
365
|
+
this.resourceName.type = this.type;
|
|
366
|
+
}
|
|
367
|
+
if (this.resourceName.prefix === '') {
|
|
368
|
+
this.resourceName.prefix = this.project.projectPrefix;
|
|
369
|
+
}
|
|
370
|
+
if (this.type) {
|
|
371
|
+
this.moduleResource =
|
|
372
|
+
this.resourceName.prefix !== this.project.projectPrefix;
|
|
373
|
+
this.resourceFolder = this.moduleResource
|
|
374
|
+
? join(
|
|
375
|
+
this.project.paths.modulesFolder,
|
|
376
|
+
this.resourceName.prefix,
|
|
377
|
+
this.resourceName.type,
|
|
378
|
+
)
|
|
379
|
+
: this.project.paths.resourcePath(this.type);
|
|
380
|
+
this.fileName = resourceNameToPath(this.project, this.resourceName);
|
|
381
|
+
}
|
|
382
|
+
// Read from cache, if entry exists...
|
|
383
|
+
if (this.cache.has(resourceNameToString(this.resourceName))) {
|
|
384
|
+
this.content = this.cache.get(
|
|
385
|
+
resourceNameToString(this.resourceName),
|
|
386
|
+
) as unknown as T;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
//... otherwise read from disk and add to cache
|
|
390
|
+
try {
|
|
391
|
+
this.content = readJsonFileSync(this.fileName);
|
|
392
|
+
this.toCache();
|
|
393
|
+
} catch {
|
|
394
|
+
// do nothing, it is possible that file has not been created yet.
|
|
395
|
+
this.logger.info(
|
|
396
|
+
`Initializing resource '${resourceNameToString(this.resourceName)}' failed: failed to read file '${this.fileName}'`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Called after inherited class has finished 'update' operation.
|
|
402
|
+
protected async postUpdate<Type, K extends string>(
|
|
403
|
+
content: T,
|
|
404
|
+
updateKey: UpdateKey<K>,
|
|
405
|
+
op: Operation<Type>,
|
|
406
|
+
) {
|
|
407
|
+
function toValue(op: Operation<Type>) {
|
|
408
|
+
if (op.name === 'rank') return op.newIndex;
|
|
409
|
+
if (op.name === 'add') return JSON.stringify(op.target);
|
|
410
|
+
if (op.name === 'remove') return JSON.stringify(op.target);
|
|
411
|
+
if (op.name === 'change') return JSON.stringify(op.to);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Check that new name is valid.
|
|
415
|
+
if (op.name === 'change' && updateKey.key === 'name') {
|
|
416
|
+
const newName = resourceName(
|
|
417
|
+
(op as ChangeOperation<string>).to as string,
|
|
418
|
+
);
|
|
419
|
+
content.name = await this.validName(newName);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Once changes have been made; validate the content.
|
|
423
|
+
try {
|
|
424
|
+
await this.validate(content);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
if (error instanceof Error) {
|
|
427
|
+
const errorValue = typeof op === 'object' ? toValue(op) : op;
|
|
428
|
+
throw new Error(
|
|
429
|
+
`Cannot ${op.name} '${updateKey.key}' --> '${errorValue}: ${error.message}'`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
this.content = content;
|
|
435
|
+
await this.write();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Update resource; the base class makes some checks only.
|
|
439
|
+
protected async update<Type, K extends string>(
|
|
440
|
+
key: UpdateKey<K>,
|
|
441
|
+
_op: Operation<Type>,
|
|
442
|
+
): Promise<void> {
|
|
443
|
+
const content = this.data;
|
|
444
|
+
if (!content) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
`Resource '${resourceNameToString(this.resourceName)}' does not exist`,
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
if (this.moduleResource) {
|
|
450
|
+
throw new Error(`Cannot update module resources`);
|
|
451
|
+
}
|
|
452
|
+
if (key.key === '' || key === undefined) {
|
|
453
|
+
throw new Error(`Cannot update empty key`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Reads content from file to memory.
|
|
458
|
+
protected async read() {
|
|
459
|
+
this.content = await readJsonFile(this.fileName);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Renames resource.
|
|
463
|
+
protected async rename(newName: ResourceName) {
|
|
464
|
+
if (this.moduleResource) {
|
|
465
|
+
throw new Error(`Cannot rename module resources`);
|
|
466
|
+
}
|
|
467
|
+
if (!pathExists(this.fileName)) {
|
|
468
|
+
throw new Error(
|
|
469
|
+
`Resource '${this.resourceName.identifier}' does not exist`,
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
if (newName.prefix !== this.project.projectPrefix) {
|
|
473
|
+
throw new Error('Can only rename project resources');
|
|
474
|
+
}
|
|
475
|
+
if (newName.type !== this.resourceName.type) {
|
|
476
|
+
throw new Error('Cannot change resource type');
|
|
477
|
+
}
|
|
478
|
+
const validator = await ResourceObject.getValidate();
|
|
479
|
+
validator.validResourceName(
|
|
480
|
+
this.resourceType(),
|
|
481
|
+
resourceNameToString(newName),
|
|
482
|
+
await this.project.projectPrefixes(),
|
|
483
|
+
);
|
|
484
|
+
const newFilename = join(
|
|
485
|
+
this.project.paths.resourcePath(newName.type as ResourceFolderType),
|
|
486
|
+
newName.identifier + '.json',
|
|
487
|
+
);
|
|
488
|
+
await rename(this.fileName, newFilename);
|
|
489
|
+
|
|
490
|
+
this.cache.delete(resourceNameToString(this.resourceName));
|
|
491
|
+
this.fileName = newFilename;
|
|
492
|
+
this.content.name = resourceNameToString(newName);
|
|
493
|
+
this.resourceName = newName;
|
|
494
|
+
this.toCache();
|
|
201
495
|
}
|
|
202
496
|
|
|
203
497
|
/**
|
|
@@ -284,4 +578,113 @@ export class ResourceObject extends AbstractResource {
|
|
|
284
578
|
}),
|
|
285
579
|
);
|
|
286
580
|
}
|
|
581
|
+
|
|
582
|
+
// Check if there are references to the resource in the card content.
|
|
583
|
+
// @note that this needs to be async, since inherited classes need to async operations
|
|
584
|
+
protected async usage(cards?: Card[]): Promise<string[]> {
|
|
585
|
+
if (!pathExists(this.fileName)) {
|
|
586
|
+
throw new Error(
|
|
587
|
+
`Resource '${this.resourceName.identifier}' does not exist in the project`,
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
const cardArray = cards?.length ? cards : this.project.cards(undefined);
|
|
591
|
+
|
|
592
|
+
return cardArray
|
|
593
|
+
.filter((card) =>
|
|
594
|
+
card.content?.includes(resourceNameToString(this.resourceName)),
|
|
595
|
+
)
|
|
596
|
+
.map((card) => card.key);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
protected async validName(newName: ResourceName) {
|
|
600
|
+
const validator = await ResourceObject.getValidate();
|
|
601
|
+
const validName = validator.validResourceName(
|
|
602
|
+
this.resourceType(),
|
|
603
|
+
resourceNameToString(newName),
|
|
604
|
+
await this.project.projectPrefixes(),
|
|
605
|
+
);
|
|
606
|
+
return validName;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Write the content from memory to disk.
|
|
610
|
+
protected async write() {
|
|
611
|
+
if (this.moduleResource) {
|
|
612
|
+
throw new Error(`Cannot change module resources`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Create folder for resources and add correct .schema file.
|
|
616
|
+
await mkdir(this.resourceFolder, { recursive: true });
|
|
617
|
+
await writeJsonFile(
|
|
618
|
+
join(this.resourceFolder, '.schema'),
|
|
619
|
+
this.contentSchema,
|
|
620
|
+
{
|
|
621
|
+
flag: 'wx',
|
|
622
|
+
},
|
|
623
|
+
);
|
|
624
|
+
// Check if "name" has changed. Changing "name" means renaming the file.
|
|
625
|
+
const nameInContent = resourceName(this.content.name).identifier + '.json';
|
|
626
|
+
const currentFileName = basename(this.fileName);
|
|
627
|
+
|
|
628
|
+
if (nameInContent !== currentFileName) {
|
|
629
|
+
const newFileName = join(this.resourceFolder, nameInContent);
|
|
630
|
+
await rename(this.fileName, newFileName);
|
|
631
|
+
this.fileName = newFileName;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
await writeJsonFile(this.fileName, this.content);
|
|
635
|
+
this.toCache();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Returns memory resident data as JSON.
|
|
640
|
+
* This is basically same as 'show' but doesn't do any checks; just returns the current content.
|
|
641
|
+
* @returns metadata content or undefined if resource does not exist.
|
|
642
|
+
*/
|
|
643
|
+
public get data() {
|
|
644
|
+
return this.content.name !== '' ? this.content : undefined;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Deletes the file and removes the resource from project.
|
|
649
|
+
* @throws if resource is a module resource or does not exist or is used by other resources.
|
|
650
|
+
*/
|
|
651
|
+
public async delete() {
|
|
652
|
+
if (this.moduleResource) {
|
|
653
|
+
throw new Error(
|
|
654
|
+
`Cannot delete resource ${resourceNameToString(this.resourceName)}: It is a module resource`,
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (!this.fileName.endsWith('.json')) {
|
|
658
|
+
this.fileName += '.json';
|
|
659
|
+
}
|
|
660
|
+
if (!pathExists(this.fileName)) {
|
|
661
|
+
throw new Error(
|
|
662
|
+
`Resource '${this.resourceName.identifier}' does not exist in the project`,
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const usedIn = await this.usage();
|
|
666
|
+
if (usedIn.length > 0) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
`Cannot delete resource ${resourceNameToString(this.resourceName)}. It is used by: ${usedIn.join(', ')}`,
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
await deleteFile(this.fileName);
|
|
672
|
+
this.project.removeResource(this.resourceObjectToResource());
|
|
673
|
+
this.fileName = '';
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Validates the content of the resource.
|
|
678
|
+
* @param content Content to be validated.
|
|
679
|
+
*/
|
|
680
|
+
public async validate(content?: object) {
|
|
681
|
+
const validator = await ResourceObject.getValidate();
|
|
682
|
+
const invalidJson = validator.validateJson(
|
|
683
|
+
content ? content : this.content,
|
|
684
|
+
this.contentSchemaId,
|
|
685
|
+
);
|
|
686
|
+
if (invalidJson.length) {
|
|
687
|
+
throw new Error(`Invalid content JSON: ${invalidJson}`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
287
690
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
15
|
import { mkdir } from 'node:fs/promises';
|
|
16
16
|
|
|
17
17
|
import type {
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import type {
|
|
30
30
|
TemplateConfiguration,
|
|
31
31
|
TemplateMetadata,
|
|
32
|
+
UpdateKey,
|
|
32
33
|
} from '../interfaces/resource-interfaces.js';
|
|
33
34
|
import { Template } from '../containers/template.js';
|
|
34
35
|
import { writeJsonFile } from '../utils/json.js';
|
|
@@ -36,7 +37,7 @@ import { writeJsonFile } from '../utils/json.js';
|
|
|
36
37
|
/**
|
|
37
38
|
* Template resource class.
|
|
38
39
|
*/
|
|
39
|
-
export class TemplateResource extends FolderResource {
|
|
40
|
+
export class TemplateResource extends FolderResource<TemplateMetadata, never> {
|
|
40
41
|
private cardContainer: Template;
|
|
41
42
|
private cardsFolder = '';
|
|
42
43
|
private cardsSchema = super.contentSchemaContent('cardBaseSchema');
|
|
@@ -47,12 +48,11 @@ export class TemplateResource extends FolderResource {
|
|
|
47
48
|
this.contentSchemaId = 'templateSchema';
|
|
48
49
|
this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
|
|
49
50
|
|
|
50
|
-
this.initialize();
|
|
51
51
|
this.cardsFolder = join(this.internalFolder, 'c');
|
|
52
52
|
|
|
53
53
|
// Each template resource contains a template card container (with template cards).
|
|
54
54
|
this.cardContainer = new Template(this.project, {
|
|
55
|
-
name:
|
|
55
|
+
name: resourceNameToString(this.resourceName),
|
|
56
56
|
path: dirname(this.fileName),
|
|
57
57
|
});
|
|
58
58
|
}
|
|
@@ -86,13 +86,6 @@ export class TemplateResource extends FolderResource {
|
|
|
86
86
|
return super.create(newContent);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
/**
|
|
90
|
-
* Returns content data.
|
|
91
|
-
*/
|
|
92
|
-
public get data(): TemplateMetadata {
|
|
93
|
-
return super.data as TemplateMetadata;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
89
|
/**
|
|
97
90
|
* Deletes file and folder that this resource is based on.
|
|
98
91
|
*/
|
|
@@ -115,7 +108,7 @@ export class TemplateResource extends FolderResource {
|
|
|
115
108
|
* @returns template metadata.
|
|
116
109
|
*/
|
|
117
110
|
public async show(): Promise<TemplateConfiguration> {
|
|
118
|
-
const templateMetadata =
|
|
111
|
+
const templateMetadata = await super.show();
|
|
119
112
|
const container = this.templateObject();
|
|
120
113
|
|
|
121
114
|
return {
|
|
@@ -124,7 +117,7 @@ export class TemplateResource extends FolderResource {
|
|
|
124
117
|
displayName: templateMetadata.displayName,
|
|
125
118
|
description: templateMetadata.description,
|
|
126
119
|
path: this.fileName,
|
|
127
|
-
numberOfCards:
|
|
120
|
+
numberOfCards: container.listCards().length,
|
|
128
121
|
};
|
|
129
122
|
}
|
|
130
123
|
|
|
@@ -138,20 +131,24 @@ export class TemplateResource extends FolderResource {
|
|
|
138
131
|
|
|
139
132
|
/**
|
|
140
133
|
* Updates template resource.
|
|
141
|
-
* @param
|
|
134
|
+
* @param updateKey Key to modify
|
|
142
135
|
* @param op Operation to perform on 'key'
|
|
143
136
|
* @throws if key is unknown.
|
|
144
137
|
*/
|
|
145
|
-
public async update<Type
|
|
138
|
+
public async update<Type, K extends string>(
|
|
139
|
+
updateKey: UpdateKey<K>,
|
|
140
|
+
op: Operation<Type>,
|
|
141
|
+
) {
|
|
142
|
+
const { key } = updateKey;
|
|
146
143
|
const nameChange = key === 'name';
|
|
147
144
|
const existingName = this.content.name;
|
|
148
145
|
|
|
149
146
|
// Only call super.update for keys that base class supports
|
|
150
147
|
if (key === 'name' || key === 'displayName' || key === 'description') {
|
|
151
|
-
await super.update(
|
|
148
|
+
await super.update(updateKey, op);
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
const content = structuredClone(this.content)
|
|
151
|
+
const content = structuredClone(this.content);
|
|
155
152
|
|
|
156
153
|
if (key === 'name') {
|
|
157
154
|
content.name = super.handleScalar(op) as string;
|
|
@@ -165,7 +162,7 @@ export class TemplateResource extends FolderResource {
|
|
|
165
162
|
throw new Error(`Unknown property '${key}' for Template`);
|
|
166
163
|
}
|
|
167
164
|
|
|
168
|
-
await super.postUpdate(content,
|
|
165
|
+
await super.postUpdate(content, updateKey, op);
|
|
169
166
|
|
|
170
167
|
// Renaming this template causes that references to its name must be updated.
|
|
171
168
|
if (nameChange) {
|
|
@@ -181,7 +178,7 @@ export class TemplateResource extends FolderResource {
|
|
|
181
178
|
* @returns array of card keys, and calculation filenames that refer this resource.
|
|
182
179
|
*/
|
|
183
180
|
public async usage(cards?: Card[]): Promise<string[]> {
|
|
184
|
-
const allCards = cards ??
|
|
181
|
+
const allCards = cards ?? super.cards();
|
|
185
182
|
const [relevantCards, calculations] = await Promise.all([
|
|
186
183
|
super.usage(allCards),
|
|
187
184
|
super.calculations(),
|
|
@@ -189,16 +186,6 @@ export class TemplateResource extends FolderResource {
|
|
|
189
186
|
return [...relevantCards.sort(sortCards), ...calculations];
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
/**
|
|
193
|
-
* Validates template.
|
|
194
|
-
* @throws when there are validation errors.
|
|
195
|
-
* @param content Content to be validated.
|
|
196
|
-
* @note If content is not provided, base class validation will use resource's current content.
|
|
197
|
-
*/
|
|
198
|
-
public async validate(content?: object) {
|
|
199
|
-
return super.validate(content);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
189
|
/**
|
|
203
190
|
* Create the template's cards folder.
|
|
204
191
|
*/
|