@cyberismo/data-handler 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card-metadata-updater.js +1 -3
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.js +13 -17
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +1 -1
- package/dist/command-manager.js +4 -3
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.d.ts +3 -3
- package/dist/commands/create.js +20 -81
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.d.ts +12 -25
- package/dist/commands/edit.js +25 -74
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/export.js +4 -17
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/fetch.js +2 -1
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/import.js +3 -5
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/move.d.ts +1 -2
- package/dist/commands/move.js +108 -146
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.js +15 -49
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.d.ts +1 -0
- package/dist/commands/rename.js +13 -7
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +7 -25
- package/dist/commands/show.js +39 -113
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.js +27 -30
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.d.ts +5 -3
- package/dist/commands/update.js +19 -5
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.d.ts +3 -3
- package/dist/commands/validate.js +20 -27
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.d.ts +87 -24
- package/dist/containers/card-container.js +183 -279
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/calculation-engine.d.ts +6 -0
- package/dist/containers/project/calculation-engine.js +36 -29
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project/card-cache.d.ts +146 -0
- package/dist/containers/project/card-cache.js +411 -0
- package/dist/containers/project/card-cache.js.map +1 -0
- package/dist/containers/project/resource-collector.d.ts +24 -1
- package/dist/containers/project/resource-collector.js +8 -1
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +119 -84
- package/dist/containers/project.js +423 -253
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +15 -31
- package/dist/containers/template.js +97 -104
- package/dist/containers/template.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/folder-content-interfaces.d.ts +12 -5
- package/dist/interfaces/folder-content-interfaces.js +5 -3
- package/dist/interfaces/folder-content-interfaces.js.map +1 -1
- package/dist/interfaces/macros.d.ts +1 -0
- package/dist/interfaces/macros.js +1 -1
- package/dist/interfaces/macros.js.map +1 -1
- package/dist/interfaces/project-interfaces.d.ts +16 -10
- package/dist/interfaces/project-interfaces.js +10 -8
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +21 -22
- package/dist/interfaces/resource-interfaces.js +3 -0
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/common.d.ts +10 -10
- package/dist/macros/createCards/index.d.ts +0 -13
- package/dist/macros/createCards/index.js.map +1 -1
- package/dist/macros/createCards/types.d.ts +44 -0
- package/dist/macros/createCards/types.js +15 -0
- package/dist/macros/createCards/types.js.map +1 -0
- package/dist/macros/graph/index.d.ts +2 -6
- package/dist/macros/graph/index.js +2 -2
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/graph/types.d.ts +23 -0
- package/dist/macros/graph/types.js +15 -0
- package/dist/macros/graph/types.js.map +1 -0
- package/dist/macros/image/index.d.ts +8 -16
- package/dist/macros/image/index.js +36 -33
- package/dist/macros/image/index.js.map +1 -1
- package/dist/macros/image/types.d.ts +38 -0
- package/dist/macros/image/types.js +15 -0
- package/dist/macros/image/types.js.map +1 -0
- package/dist/macros/include/index.d.ts +1 -6
- package/dist/macros/include/index.js +4 -7
- package/dist/macros/include/index.js.map +1 -1
- package/dist/macros/include/types.d.ts +31 -0
- package/dist/macros/include/types.js +15 -0
- package/dist/macros/include/types.js.map +1 -0
- package/dist/macros/percentage/index.d.ts +0 -6
- package/dist/macros/percentage/index.js.map +1 -1
- package/dist/macros/percentage/types.d.ts +31 -0
- package/dist/macros/percentage/types.js +15 -0
- package/dist/macros/percentage/types.js.map +1 -0
- package/dist/macros/report/index.d.ts +0 -3
- package/dist/macros/report/index.js.map +1 -1
- package/dist/macros/report/types.d.ts +19 -0
- package/dist/macros/report/types.js +15 -0
- package/dist/macros/report/types.js.map +1 -0
- package/dist/macros/scoreCard/index.d.ts +0 -6
- package/dist/macros/scoreCard/index.js.map +1 -1
- package/dist/macros/scoreCard/types.d.ts +31 -0
- package/dist/macros/scoreCard/types.js +15 -0
- package/dist/macros/scoreCard/types.js.map +1 -0
- package/dist/macros/types.d.ts +25 -0
- package/dist/macros/types.js +2 -0
- package/dist/macros/types.js.map +1 -0
- package/dist/macros/vega/index.d.ts +0 -4
- package/dist/macros/vega/index.js.map +1 -1
- package/dist/macros/vega/types.d.ts +20 -0
- package/dist/macros/vega/types.js +2 -0
- package/dist/macros/vega/types.js.map +1 -0
- package/dist/macros/vegalite/index.d.ts +0 -4
- package/dist/macros/vegalite/index.js.map +1 -1
- package/dist/macros/vegalite/types.d.ts +20 -0
- package/dist/macros/vegalite/types.js +15 -0
- package/dist/macros/vegalite/types.js.map +1 -0
- package/dist/macros/xref/index.d.ts +0 -3
- package/dist/macros/xref/index.js +5 -14
- package/dist/macros/xref/index.js.map +1 -1
- package/dist/macros/xref/types.d.ts +19 -0
- package/dist/macros/xref/types.js +15 -0
- package/dist/macros/xref/types.js.map +1 -0
- package/dist/module-manager.js +4 -4
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +43 -0
- package/dist/resources/calculation-resource.js +75 -0
- package/dist/resources/calculation-resource.js.map +1 -0
- package/dist/resources/card-type-resource.d.ts +4 -21
- package/dist/resources/card-type-resource.js +13 -44
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/create-defaults.d.ts +13 -6
- package/dist/resources/create-defaults.js +19 -5
- package/dist/resources/create-defaults.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +4 -21
- package/dist/resources/field-type-resource.js +14 -38
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +12 -29
- package/dist/resources/file-resource.js +19 -287
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +32 -51
- package/dist/resources/folder-resource.js +68 -96
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +5 -33
- package/dist/resources/graph-model-resource.js +8 -61
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +5 -28
- package/dist/resources/graph-view-resource.js +6 -45
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +4 -21
- package/dist/resources/link-type-resource.js +6 -31
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +5 -17
- package/dist/resources/report-resource.js +6 -44
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +58 -23
- package/dist/resources/resource-object.js +307 -26
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +4 -15
- package/dist/resources/template-resource.js +10 -25
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +4 -23
- package/dist/resources/workflow-resource.js +12 -38
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/card-utils.d.ts +69 -19
- package/dist/utils/card-utils.js +179 -30
- package/dist/utils/card-utils.js.map +1 -1
- package/dist/utils/clingo-facts.js +11 -3
- package/dist/utils/clingo-facts.js.map +1 -1
- package/dist/utils/clingo-parser.js +1 -1
- package/dist/utils/clingo-parser.js.map +1 -1
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/csv.js +1 -1
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/error-utils.d.ts +34 -0
- package/dist/utils/error-utils.js +56 -0
- package/dist/utils/error-utils.js.map +1 -0
- package/dist/utils/log-utils.d.ts +0 -27
- package/dist/utils/log-utils.js +0 -58
- package/dist/utils/log-utils.js.map +1 -1
- package/dist/utils/user-preferences.js +6 -3
- package/dist/utils/user-preferences.js.map +1 -1
- package/package.json +5 -5
- package/src/card-metadata-updater.ts +3 -5
- package/src/command-handler.ts +14 -19
- package/src/command-manager.ts +4 -3
- package/src/commands/create.ts +28 -112
- package/src/commands/edit.ts +27 -118
- package/src/commands/export.ts +8 -29
- package/src/commands/fetch.ts +2 -1
- package/src/commands/import.ts +4 -6
- package/src/commands/move.ts +144 -179
- package/src/commands/remove.ts +12 -54
- package/src/commands/rename.ts +22 -7
- package/src/commands/show.ts +51 -156
- package/src/commands/transition.ts +30 -33
- package/src/commands/update.ts +27 -9
- package/src/commands/validate.ts +22 -37
- package/src/containers/card-container.ts +200 -360
- package/src/containers/project/calculation-engine.ts +43 -33
- package/src/containers/project/card-cache.ts +497 -0
- package/src/containers/project/resource-collector.ts +9 -1
- package/src/containers/project.ts +533 -328
- package/src/containers/template.ts +109 -127
- package/src/index.ts +1 -0
- package/src/interfaces/folder-content-interfaces.ts +23 -5
- package/src/interfaces/macros.ts +2 -0
- package/src/interfaces/project-interfaces.ts +19 -10
- package/src/interfaces/resource-interfaces.ts +22 -24
- package/src/macros/createCards/index.ts +1 -12
- package/src/macros/createCards/types.ts +46 -0
- package/src/macros/graph/index.ts +3 -7
- package/src/macros/graph/types.ts +24 -0
- package/src/macros/image/index.ts +50 -61
- package/src/macros/image/types.ts +39 -0
- package/src/macros/include/index.ts +6 -15
- package/src/macros/include/types.ts +32 -0
- package/src/macros/percentage/index.ts +1 -7
- package/src/macros/percentage/types.ts +32 -0
- package/src/macros/report/index.ts +1 -4
- package/src/macros/report/types.ts +20 -0
- package/src/macros/scoreCard/index.ts +1 -7
- package/src/macros/scoreCard/types.ts +32 -0
- package/src/macros/types.ts +48 -0
- package/src/macros/vega/index.ts +1 -4
- package/src/macros/vega/types.ts +21 -0
- package/src/macros/vegalite/index.ts +1 -4
- package/src/macros/vegalite/types.ts +22 -0
- package/src/macros/xref/index.ts +6 -20
- package/src/macros/xref/types.ts +20 -0
- package/src/module-manager.ts +5 -5
- package/src/project-settings.ts +1 -1
- package/src/resources/calculation-resource.ts +101 -0
- package/src/resources/card-type-resource.ts +24 -59
- package/src/resources/create-defaults.ts +21 -5
- package/src/resources/field-type-resource.ts +22 -51
- package/src/resources/file-resource.ts +27 -403
- package/src/resources/folder-resource.ts +99 -125
- package/src/resources/graph-model-resource.ts +17 -74
- package/src/resources/graph-view-resource.ts +14 -54
- package/src/resources/link-type-resource.ts +13 -40
- package/src/resources/report-resource.ts +17 -57
- package/src/resources/resource-object.ts +454 -39
- package/src/resources/template-resource.ts +16 -29
- package/src/resources/workflow-resource.ts +26 -50
- package/src/utils/card-utils.ts +217 -31
- package/src/utils/clingo-facts.ts +13 -3
- package/src/utils/clingo-parser.ts +1 -1
- package/src/utils/constants.ts +7 -0
- package/src/utils/csv.ts +1 -1
- package/src/utils/error-utils.ts +62 -0
- package/src/utils/log-utils.ts +0 -68
- package/src/utils/user-preferences.ts +7 -3
|
@@ -13,19 +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
|
+
|
|
19
|
+
import { hasCode } from '../utils/error-utils.js';
|
|
18
20
|
|
|
19
21
|
import { ArrayHandler } from './array-handler.js';
|
|
20
22
|
import type {
|
|
21
23
|
Card,
|
|
24
|
+
Resource,
|
|
22
25
|
ResourceFolderType,
|
|
23
26
|
} from '../interfaces/project-interfaces.js';
|
|
24
27
|
import type { Logger } from 'pino';
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
27
|
-
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';
|
|
28
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';
|
|
29
48
|
|
|
30
49
|
// Possible operations to perform when doing "update"
|
|
31
50
|
export type UpdateOperations = 'add' | 'change' | 'rank' | 'remove';
|
|
@@ -78,18 +97,29 @@ export type OperationMap<T> = {
|
|
|
78
97
|
// Given an operation name, get the corresponding operation type
|
|
79
98
|
export type OperationFor<T, N extends UpdateOperations> = OperationMap<T>[N];
|
|
80
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
|
+
|
|
81
108
|
/**
|
|
82
109
|
* Abstract class for resources.
|
|
83
110
|
*/
|
|
84
|
-
export abstract class AbstractResource
|
|
111
|
+
export abstract class AbstractResource<
|
|
112
|
+
T extends ResourceBaseMetadata,
|
|
113
|
+
U = never, // determines type returned by show()
|
|
114
|
+
> {
|
|
85
115
|
protected abstract calculate(): Promise<void>; // update resource specific calculations
|
|
86
|
-
protected abstract create(content?:
|
|
116
|
+
protected abstract create(content?: T): Promise<void>; // create a new with the content (memory)
|
|
87
117
|
protected abstract delete(): Promise<void>; // delete from disk
|
|
88
118
|
protected abstract read(): Promise<void>; // read content from disk (replaces existing content, if any)
|
|
89
119
|
protected abstract rename(newName: ResourceName): Promise<void>; // change name of the resource and filename; same as update('name', ...)
|
|
90
|
-
protected abstract show(): Promise<
|
|
91
|
-
protected abstract update<Type>(
|
|
92
|
-
|
|
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>,
|
|
93
123
|
operation: Operation<Type>,
|
|
94
124
|
): Promise<void>; // change one key of resource
|
|
95
125
|
protected abstract usage(cards?: Card[]): Promise<string[]>; // list of card keys or resource names where this resource is used in
|
|
@@ -100,47 +130,125 @@ export abstract class AbstractResource {
|
|
|
100
130
|
protected abstract getLogger(loggerName: string): Logger;
|
|
101
131
|
}
|
|
102
132
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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;
|
|
107
144
|
protected moduleResource: boolean;
|
|
108
145
|
protected contentSchema: JSON = {} as JSON;
|
|
109
146
|
protected contentSchemaId: string = '';
|
|
110
147
|
protected type: ResourceFolderType = '' as ResourceFolderType;
|
|
111
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 = '';
|
|
112
155
|
|
|
113
156
|
constructor(
|
|
114
157
|
protected project: Project,
|
|
115
158
|
protected resourceName: ResourceName,
|
|
159
|
+
type: ResourceFolderType,
|
|
116
160
|
) {
|
|
117
161
|
super();
|
|
118
162
|
this.moduleResource =
|
|
119
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
|
|
120
168
|
}
|
|
121
169
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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;
|
|
129
178
|
}
|
|
130
|
-
|
|
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 {
|
|
131
189
|
return this.type;
|
|
132
190
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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;
|
|
137
243
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
244
|
+
|
|
245
|
+
// Cards from project.
|
|
246
|
+
protected cards(): Card[] {
|
|
247
|
+
return [
|
|
248
|
+
...this.project.cards(undefined),
|
|
249
|
+
...this.project.allTemplateCards(),
|
|
250
|
+
];
|
|
141
251
|
}
|
|
142
|
-
protected async validate(_content?: object) {}
|
|
143
|
-
protected async write() {}
|
|
144
252
|
|
|
145
253
|
/**
|
|
146
254
|
* Returns .schema content file.
|
|
@@ -156,6 +264,59 @@ export class ResourceObject extends AbstractResource {
|
|
|
156
264
|
] as unknown as JSON;
|
|
157
265
|
}
|
|
158
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
|
+
|
|
159
320
|
/**
|
|
160
321
|
* Handles operation to an array.
|
|
161
322
|
* @param operation Operation to perform on array.
|
|
@@ -195,7 +356,142 @@ export class ResourceObject extends AbstractResource {
|
|
|
195
356
|
) {
|
|
196
357
|
throw new Error(`Cannot do operation ${operation.name} on scalar value`);
|
|
197
358
|
}
|
|
198
|
-
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();
|
|
199
495
|
}
|
|
200
496
|
|
|
201
497
|
/**
|
|
@@ -222,19 +518,29 @@ export class ResourceObject extends AbstractResource {
|
|
|
222
518
|
);
|
|
223
519
|
}
|
|
224
520
|
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
521
|
+
const base = basename(calculation.name);
|
|
522
|
+
const fileNameWithExtension = base.endsWith('.lp')
|
|
523
|
+
? base
|
|
524
|
+
: base + '.lp';
|
|
525
|
+
const filename = join(calculation.path, fileNameWithExtension);
|
|
229
526
|
|
|
230
527
|
try {
|
|
231
528
|
const content = await readFile(filename, 'utf-8');
|
|
232
529
|
const updatedContent = content.replaceAll(from, to);
|
|
233
530
|
await writeFile(filename, updatedContent);
|
|
234
531
|
} catch (error) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
532
|
+
if (hasCode(error) && error.code === 'ENOENT') {
|
|
533
|
+
// Skip files that don't exist (they may have been renamed or deleted)
|
|
534
|
+
this.getLogger(this.getType).warn(
|
|
535
|
+
`Skipping non-existent file: ${filename}`,
|
|
536
|
+
);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (error instanceof Error) {
|
|
540
|
+
throw new Error(
|
|
541
|
+
`Failed to process file while updating calculation ${filename}: ${error.message}`,
|
|
542
|
+
);
|
|
543
|
+
}
|
|
238
544
|
}
|
|
239
545
|
}),
|
|
240
546
|
);
|
|
@@ -272,4 +578,113 @@ export class ResourceObject extends AbstractResource {
|
|
|
272
578
|
}),
|
|
273
579
|
);
|
|
274
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
|
+
}
|
|
275
690
|
}
|