@cyberismo/data-handler 0.0.7 → 0.0.8
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/commands/create.d.ts +1 -1
- package/dist/commands/create.js +15 -10
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/import.js +8 -2
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -1
- package/dist/commands/remove.js +4 -10
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/validate.d.ts +0 -8
- package/dist/commands/validate.js +0 -32
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/project/resource-collector.d.ts +2 -1
- package/dist/containers/project/resource-collector.js +33 -23
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +0 -5
- package/dist/containers/project.js +9 -17
- package/dist/containers/project.js.map +1 -1
- package/dist/exceptions/index.d.ts +20 -0
- package/dist/exceptions/index.js +16 -0
- package/dist/exceptions/index.js.map +1 -1
- package/dist/interfaces/macros.d.ts +3 -0
- package/dist/macros/base-macro.d.ts +2 -0
- package/dist/macros/base-macro.js +66 -19
- package/dist/macros/base-macro.js.map +1 -1
- package/dist/macros/graph/index.d.ts +0 -1
- package/dist/macros/graph/index.js +11 -7
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/index.d.ts +6 -0
- package/dist/macros/index.js +25 -2
- package/dist/macros/index.js.map +1 -1
- package/dist/macros/report/index.js +18 -9
- package/dist/macros/report/index.js.map +1 -1
- package/dist/module-manager.d.ts +16 -4
- package/dist/module-manager.js +179 -55
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js +2 -8
- package/dist/project-settings.js.map +1 -1
- package/package.json +3 -4
- package/src/commands/create.ts +18 -10
- package/src/commands/import.ts +15 -2
- package/src/commands/remove.ts +7 -12
- package/src/commands/validate.ts +0 -35
- package/src/containers/project/resource-collector.ts +33 -14
- package/src/containers/project.ts +36 -18
- package/src/exceptions/index.ts +36 -0
- package/src/interfaces/macros.ts +3 -0
- package/src/macros/base-macro.ts +89 -25
- package/src/macros/graph/index.ts +12 -7
- package/src/macros/index.ts +29 -2
- package/src/macros/report/index.ts +19 -10
- package/src/module-manager.ts +228 -66
- package/src/project-settings.ts +2 -11
package/src/commands/create.ts
CHANGED
|
@@ -72,15 +72,20 @@ export class Create {
|
|
|
72
72
|
},
|
|
73
73
|
];
|
|
74
74
|
|
|
75
|
-
static gitIgnoreContent: string =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
static gitIgnoreContent: string[] = [
|
|
76
|
+
'.calc',
|
|
77
|
+
'.asciidoctor',
|
|
78
|
+
'.vscode',
|
|
79
|
+
'*.html',
|
|
80
|
+
'*.pdf',
|
|
81
|
+
'*.puml',
|
|
82
|
+
'**/.DS_Store',
|
|
83
|
+
'*-debug.log',
|
|
84
|
+
'*-error.log',
|
|
85
|
+
'.temp',
|
|
86
|
+
'.logs',
|
|
87
|
+
'.cache',
|
|
88
|
+
];
|
|
84
89
|
|
|
85
90
|
/**
|
|
86
91
|
* Adds new cards to a template.
|
|
@@ -510,7 +515,10 @@ export class Create {
|
|
|
510
515
|
});
|
|
511
516
|
|
|
512
517
|
try {
|
|
513
|
-
await writeFile(
|
|
518
|
+
await writeFile(
|
|
519
|
+
join(projectPath, '.gitignore'),
|
|
520
|
+
this.gitIgnoreContent.join('\n') + '\n',
|
|
521
|
+
);
|
|
514
522
|
} catch {
|
|
515
523
|
console.error('Failed to create project');
|
|
516
524
|
}
|
package/src/commands/import.ts
CHANGED
|
@@ -134,7 +134,10 @@ export class Import {
|
|
|
134
134
|
destination?: string,
|
|
135
135
|
options?: ModuleSettingOptions,
|
|
136
136
|
) {
|
|
137
|
-
const
|
|
137
|
+
const beforeImportValidateErrors = await Validate.getInstance().validate(
|
|
138
|
+
this.project.basePath,
|
|
139
|
+
);
|
|
140
|
+
const gitModule = source.startsWith('https') || source.startsWith('git@');
|
|
138
141
|
const modulePrefix = gitModule
|
|
139
142
|
? await this.moduleManager.importGitModule(source, options)
|
|
140
143
|
: await this.moduleManager.importFileModule(source, destination);
|
|
@@ -156,7 +159,17 @@ export class Import {
|
|
|
156
159
|
await this.moduleManager.updateModule(moduleSettings, options?.credentials);
|
|
157
160
|
|
|
158
161
|
// Add module as a dependency.
|
|
159
|
-
|
|
162
|
+
await this.project.importModule(moduleSettings);
|
|
163
|
+
|
|
164
|
+
// Validate the project after module has been imported
|
|
165
|
+
const afterImportValidateErrors = await Validate.getInstance().validate(
|
|
166
|
+
this.project.basePath,
|
|
167
|
+
);
|
|
168
|
+
if (afterImportValidateErrors.length > beforeImportValidateErrors.length) {
|
|
169
|
+
console.error(
|
|
170
|
+
`There are new validations errors after importing the module. Check the project`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
160
173
|
}
|
|
161
174
|
|
|
162
175
|
/**
|
package/src/commands/remove.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { join, sep } from 'node:path';
|
|
|
17
17
|
import { ActionGuard } from '../permissions/action-guard.js';
|
|
18
18
|
import type { Calculate } from './index.js';
|
|
19
19
|
import { deleteDir, deleteFile } from '../utils/file-utils.js';
|
|
20
|
+
import { ModuleManager } from '../module-manager.js';
|
|
20
21
|
import { Project } from '../containers/project.js';
|
|
21
22
|
import type { RemovableResourceTypes } from '../interfaces/project-interfaces.js';
|
|
22
23
|
import { resourceName } from '../utils/resource-utils.js';
|
|
@@ -27,10 +28,13 @@ const MODULES_PATH = `${sep}modules${sep}`;
|
|
|
27
28
|
* Remove command.
|
|
28
29
|
*/
|
|
29
30
|
export class Remove {
|
|
31
|
+
private moduleManager: ModuleManager;
|
|
30
32
|
constructor(
|
|
31
33
|
private project: Project,
|
|
32
34
|
private calculateCmd: Calculate,
|
|
33
|
-
) {
|
|
35
|
+
) {
|
|
36
|
+
this.moduleManager = new ModuleManager(this.project);
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
// True, if resource is a project resource
|
|
36
40
|
private projectResource(type: RemovableResourceTypes): boolean {
|
|
@@ -182,16 +186,6 @@ export class Remove {
|
|
|
182
186
|
await this.project.updateCardMetadataKey(sourceCardKey, 'links', newLinks);
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
// Removes modules from project
|
|
186
|
-
private async removeModule(moduleName: string) {
|
|
187
|
-
const module = await this.project.module(moduleName);
|
|
188
|
-
if (!module) {
|
|
189
|
-
throw new Error(`Module '${moduleName}' not found`);
|
|
190
|
-
}
|
|
191
|
-
await this.project.removeModule(moduleName);
|
|
192
|
-
await deleteDir(module.path);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
189
|
/**
|
|
196
190
|
* Removes either attachment, card, imported module, link or resource from project.
|
|
197
191
|
* @param type Type of resource
|
|
@@ -236,7 +230,8 @@ export class Remove {
|
|
|
236
230
|
else if (type == 'card') return this.removeCard(targetName);
|
|
237
231
|
else if (type == 'link')
|
|
238
232
|
return this.removeLink(targetName, rest[0], rest[1], rest.at(2));
|
|
239
|
-
else if (type == 'module')
|
|
233
|
+
else if (type == 'module')
|
|
234
|
+
return this.moduleManager.removeModule(targetName);
|
|
240
235
|
else if (type == 'label') return this.removeLabel(targetName, rest[0]);
|
|
241
236
|
}
|
|
242
237
|
throw new Error(`Unknown resource type '${type}'`);
|
package/src/commands/validate.ts
CHANGED
|
@@ -704,41 +704,6 @@ export class Validate {
|
|
|
704
704
|
return contentValidated && lengthValidated;
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
-
/**
|
|
708
|
-
* Validate schema that matches schemaId from path.
|
|
709
|
-
* @param projectPath path to schema
|
|
710
|
-
* @param schemaId schema's id
|
|
711
|
-
* @returns string containing all validation errors
|
|
712
|
-
* @todo - unused; remove?
|
|
713
|
-
*/
|
|
714
|
-
public async validateSchema(
|
|
715
|
-
projectPath: string,
|
|
716
|
-
schemaId: string,
|
|
717
|
-
): Promise<string> {
|
|
718
|
-
const validationErrors: string[] = [];
|
|
719
|
-
if (!schemaId.startsWith('/')) {
|
|
720
|
-
schemaId = '/' + schemaId;
|
|
721
|
-
}
|
|
722
|
-
const activeJsonSchema = this.validator.schemas[schemaId];
|
|
723
|
-
if (activeJsonSchema === undefined) {
|
|
724
|
-
throw new Error(`Unknown schema '${schemaId}'`);
|
|
725
|
-
} else {
|
|
726
|
-
let contentFile = '';
|
|
727
|
-
try {
|
|
728
|
-
contentFile = await readJsonFile(projectPath);
|
|
729
|
-
} catch {
|
|
730
|
-
throw new Error(`Path is not valid ${projectPath}`);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const result = this.validator.validate(contentFile, activeJsonSchema);
|
|
734
|
-
for (const error of result.errors) {
|
|
735
|
-
const msg = `Schema '${schemaId}' validation Error: ${error.message}\n`;
|
|
736
|
-
validationErrors.push(msg);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
return validationErrors.join('\n');
|
|
740
|
-
}
|
|
741
|
-
|
|
742
707
|
/**
|
|
743
708
|
* Validates that card's custom fields are according to schema and have correct data in them.
|
|
744
709
|
* @param project currently used Project
|
|
@@ -55,17 +55,31 @@ class ResourceCollection {
|
|
|
55
55
|
* @param type Resource array type to return.
|
|
56
56
|
* @returns resource array of a give type.
|
|
57
57
|
*/
|
|
58
|
-
public resourceArray(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (type === '
|
|
65
|
-
if (type === '
|
|
66
|
-
if (type === '
|
|
67
|
-
if (type === '
|
|
68
|
-
|
|
58
|
+
public resourceArray(
|
|
59
|
+
type: ResourceFolderType,
|
|
60
|
+
moduleName?: string,
|
|
61
|
+
): Resource[] {
|
|
62
|
+
let resources: Resource[] = [];
|
|
63
|
+
|
|
64
|
+
if (type === 'calculations') resources = this.calculations;
|
|
65
|
+
else if (type === 'cardTypes') resources = this.cardTypes;
|
|
66
|
+
else if (type === 'fieldTypes') resources = this.fieldTypes;
|
|
67
|
+
else if (type === 'graphViews') resources = this.graphViews;
|
|
68
|
+
else if (type === 'graphModels') resources = this.graphModels;
|
|
69
|
+
else if (type === 'linkTypes') resources = this.linkTypes;
|
|
70
|
+
else if (type === 'reports') resources = this.reports;
|
|
71
|
+
else if (type === 'templates') resources = this.templates;
|
|
72
|
+
else if (type === 'workflows') resources = this.workflows;
|
|
73
|
+
else throw new Error(`Unknown resource type '${type}'`);
|
|
74
|
+
|
|
75
|
+
if (moduleName) {
|
|
76
|
+
resources = resources.filter((item) => {
|
|
77
|
+
const { prefix } = resourceName(item.name);
|
|
78
|
+
return moduleName === prefix;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return resources;
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -162,6 +176,7 @@ export class ResourceCollector {
|
|
|
162
176
|
// Adds a resource type from all modules.
|
|
163
177
|
private async addResourcesFromModules(
|
|
164
178
|
type: ResourceFolderType,
|
|
179
|
+
moduleName?: string,
|
|
165
180
|
): Promise<Resource[]> {
|
|
166
181
|
try {
|
|
167
182
|
// 'modules' is a bit special; it is collected separately from actual resources.
|
|
@@ -174,7 +189,7 @@ export class ResourceCollector {
|
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
await this.addModuleResources();
|
|
177
|
-
return this.modules.resourceArray(type);
|
|
192
|
+
return this.modules.resourceArray(type, moduleName);
|
|
178
193
|
} catch {
|
|
179
194
|
return [];
|
|
180
195
|
}
|
|
@@ -261,10 +276,14 @@ export class ResourceCollector {
|
|
|
261
276
|
/**
|
|
262
277
|
* Collect specific resource from modules.
|
|
263
278
|
* @param type Type of resource (e.g. 'templates').
|
|
279
|
+
* @param moduleName Name of the module to collect resources from
|
|
264
280
|
* @returns array of collected items.
|
|
265
281
|
*/
|
|
266
|
-
public async collectResourcesFromModules(
|
|
267
|
-
|
|
282
|
+
public async collectResourcesFromModules(
|
|
283
|
+
type: ResourceFolderType,
|
|
284
|
+
moduleName?: string,
|
|
285
|
+
) {
|
|
286
|
+
return (await this.addResourcesFromModules(type, moduleName)).map((item) =>
|
|
268
287
|
stripExtension(item.name),
|
|
269
288
|
);
|
|
270
289
|
}
|
|
@@ -660,31 +660,58 @@ export class Project extends CardContainer {
|
|
|
660
660
|
path: modulePath,
|
|
661
661
|
cardKeyPrefix: moduleConfig.cardKeyPrefix,
|
|
662
662
|
calculations: [
|
|
663
|
-
...(await this.resources.collectResourcesFromModules(
|
|
663
|
+
...(await this.resources.collectResourcesFromModules(
|
|
664
|
+
'calculations',
|
|
665
|
+
moduleName,
|
|
666
|
+
)),
|
|
664
667
|
],
|
|
665
668
|
cardTypes: [
|
|
666
|
-
...(await this.resources.collectResourcesFromModules(
|
|
669
|
+
...(await this.resources.collectResourcesFromModules(
|
|
670
|
+
'cardTypes',
|
|
671
|
+
moduleName,
|
|
672
|
+
)),
|
|
667
673
|
],
|
|
668
674
|
fieldTypes: [
|
|
669
|
-
...(await this.resources.collectResourcesFromModules(
|
|
675
|
+
...(await this.resources.collectResourcesFromModules(
|
|
676
|
+
'fieldTypes',
|
|
677
|
+
moduleName,
|
|
678
|
+
)),
|
|
670
679
|
],
|
|
671
680
|
graphModels: [
|
|
672
|
-
...(await this.resources.collectResourcesFromModules(
|
|
681
|
+
...(await this.resources.collectResourcesFromModules(
|
|
682
|
+
'graphModels',
|
|
683
|
+
moduleName,
|
|
684
|
+
)),
|
|
673
685
|
],
|
|
674
686
|
graphViews: [
|
|
675
|
-
...(await this.resources.collectResourcesFromModules(
|
|
687
|
+
...(await this.resources.collectResourcesFromModules(
|
|
688
|
+
'graphViews',
|
|
689
|
+
moduleName,
|
|
690
|
+
)),
|
|
676
691
|
],
|
|
677
692
|
linkTypes: [
|
|
678
|
-
...(await this.resources.collectResourcesFromModules(
|
|
693
|
+
...(await this.resources.collectResourcesFromModules(
|
|
694
|
+
'linkTypes',
|
|
695
|
+
moduleName,
|
|
696
|
+
)),
|
|
679
697
|
],
|
|
680
698
|
reports: [
|
|
681
|
-
...(await this.resources.collectResourcesFromModules(
|
|
699
|
+
...(await this.resources.collectResourcesFromModules(
|
|
700
|
+
'reports',
|
|
701
|
+
moduleName,
|
|
702
|
+
)),
|
|
682
703
|
],
|
|
683
704
|
templates: [
|
|
684
|
-
...(await this.resources.collectResourcesFromModules(
|
|
705
|
+
...(await this.resources.collectResourcesFromModules(
|
|
706
|
+
'templates',
|
|
707
|
+
moduleName,
|
|
708
|
+
)),
|
|
685
709
|
],
|
|
686
710
|
workflows: [
|
|
687
|
-
...(await this.resources.collectResourcesFromModules(
|
|
711
|
+
...(await this.resources.collectResourcesFromModules(
|
|
712
|
+
'workflows',
|
|
713
|
+
moduleName,
|
|
714
|
+
)),
|
|
688
715
|
],
|
|
689
716
|
};
|
|
690
717
|
}
|
|
@@ -823,15 +850,6 @@ export class Project extends CardContainer {
|
|
|
823
850
|
return prefixes;
|
|
824
851
|
}
|
|
825
852
|
|
|
826
|
-
/**
|
|
827
|
-
* Removes a module from project.
|
|
828
|
-
* @param moduleName Name of the module
|
|
829
|
-
*/
|
|
830
|
-
public async removeModule(moduleName: string) {
|
|
831
|
-
await this.configuration.removeModule(moduleName);
|
|
832
|
-
await this.collectModuleResources();
|
|
833
|
-
}
|
|
834
|
-
|
|
835
853
|
/**
|
|
836
854
|
* Array of reports in the project.
|
|
837
855
|
* @param from Defines where resources are collected from.
|
package/src/exceptions/index.ts
CHANGED
|
@@ -27,3 +27,39 @@ export class SchemaNotFound extends Error {
|
|
|
27
27
|
this.name = 'SchemaNotFound';
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Stores the context of a macro error that originated from another macro
|
|
32
|
+
*/
|
|
33
|
+
export interface MacroDependency {
|
|
34
|
+
macroName: string;
|
|
35
|
+
parameters: string;
|
|
36
|
+
output?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when a macro fails to execute.
|
|
40
|
+
*/
|
|
41
|
+
export class MacroError extends Error {
|
|
42
|
+
public context: {
|
|
43
|
+
cardKey: string;
|
|
44
|
+
macroName: string;
|
|
45
|
+
parameters: string;
|
|
46
|
+
dependency?: MacroDependency;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
message: string,
|
|
51
|
+
cardKey: string,
|
|
52
|
+
macroName: string,
|
|
53
|
+
parameters: string,
|
|
54
|
+
dependency?: MacroDependency,
|
|
55
|
+
) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = 'MacroError';
|
|
58
|
+
this.context = {
|
|
59
|
+
cardKey,
|
|
60
|
+
macroName,
|
|
61
|
+
parameters,
|
|
62
|
+
dependency,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/interfaces/macros.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import type { macroMetadata } from '../macros/common.js';
|
|
14
14
|
import type { Project } from '../containers/project.js';
|
|
15
|
+
import type { MacroError } from '../exceptions/index.js';
|
|
15
16
|
|
|
16
17
|
type Mode = 'validate' | 'static' | 'inject';
|
|
17
18
|
|
|
@@ -44,6 +45,8 @@ export interface MacroTaskState {
|
|
|
44
45
|
placeholder: string;
|
|
45
46
|
promiseResult: string | null;
|
|
46
47
|
macro: string;
|
|
48
|
+
parameters: string;
|
|
49
|
+
error: MacroError | null;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
// Handlebars options is not documented
|
package/src/macros/base-macro.ts
CHANGED
|
@@ -17,13 +17,23 @@ import type {
|
|
|
17
17
|
MacroTaskState,
|
|
18
18
|
} from '../interfaces/macros.js';
|
|
19
19
|
import { generateRandomString } from '../utils/random.js';
|
|
20
|
-
import {
|
|
20
|
+
import { MacroError } from '../exceptions/index.js';
|
|
21
21
|
import type TaskQueue from './task-queue.js';
|
|
22
|
+
import { ClingoError } from '@cyberismo/node-clingo';
|
|
23
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
22
24
|
|
|
23
25
|
abstract class BaseMacro {
|
|
24
26
|
private globalId: string;
|
|
25
27
|
private localCounter: number = 0;
|
|
26
28
|
|
|
29
|
+
// Macros share the same logger
|
|
30
|
+
protected get logger() {
|
|
31
|
+
return getChildLogger({
|
|
32
|
+
module: 'macro',
|
|
33
|
+
macro: this.macroMetadata.name,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
constructor(
|
|
28
38
|
protected macroMetadata: MacroMetadata,
|
|
29
39
|
private readonly tasks: TaskQueue,
|
|
@@ -64,7 +74,7 @@ abstract class BaseMacro {
|
|
|
64
74
|
if (task) {
|
|
65
75
|
dependencies.push(task);
|
|
66
76
|
} else {
|
|
67
|
-
|
|
77
|
+
this.logger.warn(
|
|
68
78
|
`Dependency not found for placeholder: ${placeholder} (globalId: ${globalId}, localId: ${localId})`,
|
|
69
79
|
);
|
|
70
80
|
}
|
|
@@ -81,6 +91,16 @@ abstract class BaseMacro {
|
|
|
81
91
|
};
|
|
82
92
|
}
|
|
83
93
|
|
|
94
|
+
private findTask(globalId: string, localId: number) {
|
|
95
|
+
const task = this.tasks.find(globalId, localId);
|
|
96
|
+
if (!task) {
|
|
97
|
+
this.logger.warn(
|
|
98
|
+
`Task not found for global id ${globalId}, local id ${localId}.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return task;
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
/**
|
|
85
105
|
* Function responsible for starting the promise and storing it along with its localId.
|
|
86
106
|
*/
|
|
@@ -111,20 +131,53 @@ abstract class BaseMacro {
|
|
|
111
131
|
const dependencies = this.findDependencies(rawInput);
|
|
112
132
|
|
|
113
133
|
// Create a promise to resolve dependencies, execute the macro, and handle the results
|
|
114
|
-
const promise = Promise.
|
|
134
|
+
const promise = Promise.allSettled(dependencies.map((dep) => dep.promise))
|
|
115
135
|
.then(() => {
|
|
116
136
|
for (const dependency of dependencies) {
|
|
137
|
+
if (dependency.error) {
|
|
138
|
+
const task = this.findTask(this.globalId, localId);
|
|
139
|
+
if (task) {
|
|
140
|
+
// There could be a better way, but multi-nested macros are rare
|
|
141
|
+
task.error = new MacroError(
|
|
142
|
+
dependency.error.message,
|
|
143
|
+
context.cardKey,
|
|
144
|
+
this.metadata.name,
|
|
145
|
+
dependency.error.context.parameters,
|
|
146
|
+
{
|
|
147
|
+
macroName: dependency.macro,
|
|
148
|
+
parameters: dependency.parameters,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
117
154
|
input = input.replace(
|
|
118
155
|
dependency.placeholder,
|
|
119
156
|
dependency.promiseResult || '',
|
|
120
157
|
);
|
|
158
|
+
// parse json after each dep, so we know the exact macro which produced the error
|
|
159
|
+
try {
|
|
160
|
+
JSON.parse(input);
|
|
161
|
+
} catch {
|
|
162
|
+
const task = this.findTask(this.globalId, localId);
|
|
163
|
+
if (task) {
|
|
164
|
+
task.error = new MacroError(
|
|
165
|
+
'Invalid JSON produced by macro dependency',
|
|
166
|
+
context.cardKey,
|
|
167
|
+
this.metadata.name,
|
|
168
|
+
input,
|
|
169
|
+
{
|
|
170
|
+
macroName: dependency.macro,
|
|
171
|
+
parameters: dependency.parameters,
|
|
172
|
+
output: input,
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
121
178
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
parsed = JSON.parse(input);
|
|
125
|
-
} catch {
|
|
126
|
-
return 'Invalid JSON';
|
|
127
|
-
}
|
|
179
|
+
// This will never throw in practice, thus no need to catch
|
|
180
|
+
const parsed = JSON.parse(input);
|
|
128
181
|
|
|
129
182
|
// Select the function to execute based on context mode
|
|
130
183
|
const functionToCall =
|
|
@@ -134,28 +187,37 @@ abstract class BaseMacro {
|
|
|
134
187
|
return functionToCall(context, parsed);
|
|
135
188
|
})
|
|
136
189
|
.then((result) => {
|
|
137
|
-
|
|
190
|
+
// undefined is used to indicate that the macro did not run for some reason
|
|
191
|
+
if (result === undefined) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const task = this.findTask(this.globalId, localId);
|
|
138
195
|
if (task) {
|
|
139
196
|
task.promiseResult = result;
|
|
140
|
-
} else {
|
|
141
|
-
console.error(
|
|
142
|
-
`Task not found after execution: macro ${this.metadata.name}, local id ${localId}.`,
|
|
143
|
-
);
|
|
144
197
|
}
|
|
145
198
|
})
|
|
146
199
|
.catch((err) => {
|
|
147
|
-
|
|
200
|
+
if (!(err instanceof Error)) {
|
|
201
|
+
this.logger.error(err, 'Unknown error');
|
|
202
|
+
err = new Error('Unknown error');
|
|
203
|
+
}
|
|
204
|
+
const message =
|
|
205
|
+
err instanceof ClingoError
|
|
206
|
+
? err.details.errors.join('\n')
|
|
207
|
+
: err.message;
|
|
208
|
+
const error =
|
|
209
|
+
err instanceof MacroError
|
|
210
|
+
? err
|
|
211
|
+
: new MacroError(
|
|
212
|
+
message,
|
|
213
|
+
context.cardKey,
|
|
214
|
+
this.metadata.name,
|
|
215
|
+
input,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const task = this.findTask(this.globalId, localId);
|
|
148
219
|
if (task) {
|
|
149
|
-
task.
|
|
150
|
-
err,
|
|
151
|
-
this.metadata.name,
|
|
152
|
-
context,
|
|
153
|
-
);
|
|
154
|
-
} else {
|
|
155
|
-
console.error(
|
|
156
|
-
`Error handling task for macro ${this.metadata.name}, local id ${localId}:`,
|
|
157
|
-
err,
|
|
158
|
-
);
|
|
220
|
+
task.error = error;
|
|
159
221
|
}
|
|
160
222
|
});
|
|
161
223
|
|
|
@@ -167,6 +229,8 @@ abstract class BaseMacro {
|
|
|
167
229
|
placeholder,
|
|
168
230
|
promiseResult: null,
|
|
169
231
|
macro: this.macroMetadata.name,
|
|
232
|
+
parameters: rawInput,
|
|
233
|
+
error: null,
|
|
170
234
|
});
|
|
171
235
|
// Return the placeholder
|
|
172
236
|
return placeholder;
|
|
@@ -17,7 +17,6 @@ import type { MacroOptions } from '../index.js';
|
|
|
17
17
|
import { createImage, validateMacroContent } from '../index.js';
|
|
18
18
|
import Handlebars from 'handlebars';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
|
-
import { getChildLogger } from '../../utils/log-utils.js';
|
|
21
20
|
import type { MacroGenerationContext } from '../../interfaces/macros.js';
|
|
22
21
|
import macroMetadata from './metadata.js';
|
|
23
22
|
import { pathExists } from '../../utils/file-utils.js';
|
|
@@ -26,6 +25,7 @@ import { resourceName } from '../../utils/resource-utils.js';
|
|
|
26
25
|
import type { Schema } from 'jsonschema';
|
|
27
26
|
import { validateJson } from '../../utils/validate.js';
|
|
28
27
|
import type TaskQueue from '../task-queue.js';
|
|
28
|
+
import { ClingoError } from '@cyberismo/node-clingo';
|
|
29
29
|
|
|
30
30
|
export interface GraphOptions extends MacroOptions {
|
|
31
31
|
model: string;
|
|
@@ -33,11 +33,6 @@ export interface GraphOptions extends MacroOptions {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
class ReportMacro extends BaseMacro {
|
|
36
|
-
private get logger() {
|
|
37
|
-
return getChildLogger({
|
|
38
|
-
module: 'graphMacro',
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
36
|
constructor(tasksQueue: TaskQueue) {
|
|
42
37
|
super(macroMetadata, tasksQueue);
|
|
43
38
|
}
|
|
@@ -117,7 +112,17 @@ class ReportMacro extends BaseMacro {
|
|
|
117
112
|
const view = handlebars.compile(viewContent)(handlebarsContext);
|
|
118
113
|
|
|
119
114
|
const modelContent = await readFile(modelLocation, { encoding: 'utf-8' });
|
|
120
|
-
|
|
115
|
+
let result: string;
|
|
116
|
+
try {
|
|
117
|
+
result = await calculate.runGraph(modelContent, view);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof ClingoError) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Error running graph in view '${options.view}' in model '${options.model}': ${error.details.errors.join('\n')}`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
121
126
|
|
|
122
127
|
if (typeof result !== 'string') {
|
|
123
128
|
throw new Error(
|
package/src/macros/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ import report from './report/index.js';
|
|
|
18
18
|
import scoreCard from './scoreCard/index.js';
|
|
19
19
|
|
|
20
20
|
import { validateJson } from '../utils/validate.js';
|
|
21
|
-
import { DHValidationError } from '../exceptions/index.js';
|
|
21
|
+
import { DHValidationError, MacroError } from '../exceptions/index.js';
|
|
22
22
|
import type { AdmonitionType } from '../interfaces/adoc.js';
|
|
23
23
|
import type { Validator } from 'jsonschema';
|
|
24
24
|
import type {
|
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
import type BaseMacro from './base-macro.js';
|
|
30
30
|
import TaskQueue from './task-queue.js';
|
|
31
31
|
import type { Calculate } from '../commands/index.js';
|
|
32
|
+
import { ClingoError } from '@cyberismo/node-clingo';
|
|
32
33
|
const CURLY_LEFT = '{';
|
|
33
34
|
const CURLY_RIGHT = '}';
|
|
34
35
|
|
|
@@ -211,7 +212,14 @@ export function applyMacroResults(
|
|
|
211
212
|
context: MacroGenerationContext,
|
|
212
213
|
) {
|
|
213
214
|
for (const item of tasks) {
|
|
214
|
-
if (item.
|
|
215
|
+
if (item.error) {
|
|
216
|
+
input = input.replace(
|
|
217
|
+
item.placeholder,
|
|
218
|
+
handleMacroError(item.error, item.macro, context),
|
|
219
|
+
);
|
|
220
|
+
} // It should not be possible that promiseResult is null if there never was an error
|
|
221
|
+
// Unless the function itself returns null / undefined
|
|
222
|
+
else if (item.promiseResult == null) {
|
|
215
223
|
input = handleMacroError(
|
|
216
224
|
new Error(
|
|
217
225
|
`Tried to access result before it was resolved for ${item.placeholder}`,
|
|
@@ -240,7 +248,17 @@ export function handleMacroError(
|
|
|
240
248
|
let message = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
241
249
|
if (error instanceof DHValidationError) {
|
|
242
250
|
message = `Check json syntax of macro ${macro}: ${error.errors?.map((e) => e.message).join(', ')}`;
|
|
251
|
+
} else if (error instanceof MacroError) {
|
|
252
|
+
const { cardKey, macroName, dependency } = error.context;
|
|
253
|
+
message = `Macro error in card '${cardKey}' in macro '${macroName}':\n\n${error.message}.`;
|
|
254
|
+
|
|
255
|
+
if (dependency) {
|
|
256
|
+
message += `\n\nParameters:\n\n${context.mode === 'validate' ? dependency.parameters : createCodeBlock(dependency.parameters)}.\n\n${dependency.output ? `Output:\n\n${context.mode === 'validate' ? dependency.output : createCodeBlock(dependency.output)}` : ''}`;
|
|
257
|
+
}
|
|
258
|
+
} else if (error instanceof ClingoError) {
|
|
259
|
+
message = `Error running logic program in macro '${macro}':${error.details.errors.join('\n')}`;
|
|
243
260
|
}
|
|
261
|
+
|
|
244
262
|
if (
|
|
245
263
|
typeof error === 'object' &&
|
|
246
264
|
error != null &&
|
|
@@ -328,6 +346,15 @@ export function createAdmonition(
|
|
|
328
346
|
return `[${type}]\n.${label}\n====\n${content}\n====\n\n`;
|
|
329
347
|
}
|
|
330
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Creates a code block
|
|
351
|
+
* @param content - The content of the code block
|
|
352
|
+
* @returns The code block as a string
|
|
353
|
+
*/
|
|
354
|
+
export function createCodeBlock(content: string) {
|
|
355
|
+
return `\n\n----\n${content}\n----\n\n`;
|
|
356
|
+
}
|
|
357
|
+
|
|
331
358
|
/**
|
|
332
359
|
* Helper function for including base64 encoded images for now
|
|
333
360
|
* @param image base64 encoded image
|