@cyberismo/data-handler 0.0.6 → 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.
Files changed (175) hide show
  1. package/README.md +1 -0
  2. package/dist/card-metadata-updater.d.ts +1 -0
  3. package/dist/card-metadata-updater.js +7 -2
  4. package/dist/card-metadata-updater.js.map +1 -1
  5. package/dist/command-handler.d.ts +6 -1
  6. package/dist/command-handler.js +16 -15
  7. package/dist/command-handler.js.map +1 -1
  8. package/dist/command-manager.d.ts +15 -4
  9. package/dist/command-manager.js +41 -9
  10. package/dist/command-manager.js.map +1 -1
  11. package/dist/commands/calculate.d.ts +4 -10
  12. package/dist/commands/calculate.js +67 -78
  13. package/dist/commands/calculate.js.map +1 -1
  14. package/dist/commands/create.d.ts +1 -1
  15. package/dist/commands/create.js +15 -10
  16. package/dist/commands/create.js.map +1 -1
  17. package/dist/commands/export.js +3 -3
  18. package/dist/commands/export.js.map +1 -1
  19. package/dist/commands/import.d.ts +3 -8
  20. package/dist/commands/import.js +17 -15
  21. package/dist/commands/import.js.map +1 -1
  22. package/dist/commands/index.d.ts +1 -2
  23. package/dist/commands/index.js +1 -2
  24. package/dist/commands/index.js.map +1 -1
  25. package/dist/commands/remove.d.ts +1 -1
  26. package/dist/commands/remove.js +4 -10
  27. package/dist/commands/remove.js.map +1 -1
  28. package/dist/commands/show.d.ts +6 -3
  29. package/dist/commands/show.js +8 -5
  30. package/dist/commands/show.js.map +1 -1
  31. package/dist/commands/validate.d.ts +0 -8
  32. package/dist/commands/validate.js +6 -36
  33. package/dist/commands/validate.js.map +1 -1
  34. package/dist/containers/project/project-content-watcher.d.ts +28 -0
  35. package/dist/containers/project/project-content-watcher.js +54 -0
  36. package/dist/containers/project/project-content-watcher.js.map +1 -0
  37. package/dist/containers/project/project-paths.js +1 -1
  38. package/dist/containers/project/project-paths.js.map +1 -1
  39. package/dist/containers/project/resource-collector.d.ts +2 -1
  40. package/dist/containers/project/resource-collector.js +33 -23
  41. package/dist/containers/project/resource-collector.js.map +1 -1
  42. package/dist/containers/project.d.ts +9 -7
  43. package/dist/containers/project.js +58 -18
  44. package/dist/containers/project.js.map +1 -1
  45. package/dist/containers/template.d.ts +1 -0
  46. package/dist/containers/template.js +7 -2
  47. package/dist/containers/template.js.map +1 -1
  48. package/dist/exceptions/index.d.ts +20 -0
  49. package/dist/exceptions/index.js +16 -0
  50. package/dist/exceptions/index.js.map +1 -1
  51. package/dist/index.d.ts +2 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/interfaces/macros.d.ts +5 -1
  54. package/dist/interfaces/project-interfaces.d.ts +5 -0
  55. package/dist/interfaces/project-interfaces.js.map +1 -1
  56. package/dist/interfaces/resource-interfaces.d.ts +14 -22
  57. package/dist/interfaces/resource-interfaces.js +10 -9
  58. package/dist/interfaces/resource-interfaces.js.map +1 -1
  59. package/dist/macros/base-macro.d.ts +2 -0
  60. package/dist/macros/base-macro.js +66 -19
  61. package/dist/macros/base-macro.js.map +1 -1
  62. package/dist/macros/graph/index.d.ts +0 -1
  63. package/dist/macros/graph/index.js +16 -12
  64. package/dist/macros/graph/index.js.map +1 -1
  65. package/dist/macros/index.d.ts +30 -3
  66. package/dist/macros/index.js +36 -6
  67. package/dist/macros/index.js.map +1 -1
  68. package/dist/macros/report/index.d.ts +13 -10
  69. package/dist/macros/report/index.js +35 -38
  70. package/dist/macros/report/index.js.map +1 -1
  71. package/dist/module-manager.d.ts +32 -11
  72. package/dist/module-manager.js +301 -147
  73. package/dist/module-manager.js.map +1 -1
  74. package/dist/project-settings.js +8 -8
  75. package/dist/project-settings.js.map +1 -1
  76. package/dist/resources/array-handler.js +6 -1
  77. package/dist/resources/array-handler.js.map +1 -1
  78. package/dist/resources/card-type-resource.d.ts +13 -9
  79. package/dist/resources/card-type-resource.js +47 -23
  80. package/dist/resources/card-type-resource.js.map +1 -1
  81. package/dist/resources/create-defaults.d.ts +10 -9
  82. package/dist/resources/create-defaults.js +15 -12
  83. package/dist/resources/create-defaults.js.map +1 -1
  84. package/dist/resources/field-type-resource.d.ts +0 -1
  85. package/dist/resources/field-type-resource.js +2 -10
  86. package/dist/resources/field-type-resource.js.map +1 -1
  87. package/dist/resources/file-resource.d.ts +7 -7
  88. package/dist/resources/file-resource.js +32 -7
  89. package/dist/resources/file-resource.js.map +1 -1
  90. package/dist/resources/folder-resource.d.ts +10 -9
  91. package/dist/resources/folder-resource.js +10 -9
  92. package/dist/resources/folder-resource.js.map +1 -1
  93. package/dist/resources/report-resource.d.ts +5 -6
  94. package/dist/resources/report-resource.js +16 -7
  95. package/dist/resources/report-resource.js.map +1 -1
  96. package/dist/resources/template-resource.d.ts +5 -6
  97. package/dist/resources/template-resource.js +7 -6
  98. package/dist/resources/template-resource.js.map +1 -1
  99. package/dist/resources/workflow-resource.d.ts +15 -8
  100. package/dist/resources/workflow-resource.js +124 -8
  101. package/dist/resources/workflow-resource.js.map +1 -1
  102. package/dist/types/queries.d.ts +11 -10
  103. package/dist/types/queries.js +10 -9
  104. package/dist/types/queries.js.map +1 -1
  105. package/dist/utils/clingo-fact-builder.d.ts +1 -0
  106. package/dist/utils/clingo-fact-builder.js +8 -3
  107. package/dist/utils/clingo-fact-builder.js.map +1 -1
  108. package/dist/utils/clingo-facts.js +15 -11
  109. package/dist/utils/clingo-facts.js.map +1 -1
  110. package/dist/utils/constants.d.ts +18 -12
  111. package/dist/utils/constants.js +18 -11
  112. package/dist/utils/constants.js.map +1 -1
  113. package/dist/utils/log-utils.d.ts +15 -2
  114. package/dist/utils/log-utils.js +20 -37
  115. package/dist/utils/log-utils.js.map +1 -1
  116. package/dist/utils/report.d.ts +27 -0
  117. package/dist/utils/report.js +60 -0
  118. package/dist/utils/report.js.map +1 -0
  119. package/dist/utils/resource-utils.js +3 -0
  120. package/dist/utils/resource-utils.js.map +1 -1
  121. package/dist/utils/sanitize-svg.d.ts +3 -4
  122. package/dist/utils/sanitize-svg.js +4 -7
  123. package/dist/utils/sanitize-svg.js.map +1 -1
  124. package/dist/utils/validate.js +2 -1
  125. package/dist/utils/validate.js.map +1 -1
  126. package/package.json +9 -11
  127. package/src/card-metadata-updater.ts +7 -2
  128. package/src/command-handler.ts +23 -13
  129. package/src/command-manager.ts +54 -13
  130. package/src/commands/calculate.ts +90 -106
  131. package/src/commands/create.ts +18 -10
  132. package/src/commands/export.ts +3 -2
  133. package/src/commands/import.ts +30 -17
  134. package/src/commands/index.ts +0 -2
  135. package/src/commands/remove.ts +7 -12
  136. package/src/commands/show.ts +13 -5
  137. package/src/commands/validate.ts +14 -44
  138. package/src/containers/project/project-content-watcher.ts +65 -0
  139. package/src/containers/project/project-paths.ts +1 -1
  140. package/src/containers/project/resource-collector.ts +33 -14
  141. package/src/containers/project.ts +96 -19
  142. package/src/containers/template.ts +7 -2
  143. package/src/exceptions/index.ts +36 -0
  144. package/src/index.ts +2 -0
  145. package/src/interfaces/macros.ts +5 -1
  146. package/src/interfaces/project-interfaces.ts +8 -0
  147. package/src/interfaces/resource-interfaces.ts +15 -22
  148. package/src/macros/base-macro.ts +89 -25
  149. package/src/macros/graph/index.ts +22 -12
  150. package/src/macros/index.ts +61 -8
  151. package/src/macros/report/index.ts +42 -43
  152. package/src/module-manager.ts +383 -177
  153. package/src/project-settings.ts +9 -11
  154. package/src/resources/array-handler.ts +7 -2
  155. package/src/resources/card-type-resource.ts +61 -46
  156. package/src/resources/create-defaults.ts +15 -12
  157. package/src/resources/field-type-resource.ts +2 -17
  158. package/src/resources/file-resource.ts +46 -8
  159. package/src/resources/folder-resource.ts +11 -10
  160. package/src/resources/report-resource.ts +19 -7
  161. package/src/resources/template-resource.ts +7 -6
  162. package/src/resources/workflow-resource.ts +155 -8
  163. package/src/types/queries.ts +11 -10
  164. package/src/utils/clingo-fact-builder.ts +8 -3
  165. package/src/utils/clingo-facts.ts +18 -12
  166. package/src/utils/constants.ts +20 -12
  167. package/src/utils/log-utils.ts +24 -45
  168. package/src/utils/report.ts +86 -0
  169. package/src/utils/resource-utils.ts +4 -0
  170. package/src/utils/sanitize-svg.ts +4 -9
  171. package/src/utils/validate.ts +3 -2
  172. package/dist/commands/export-site.d.ts +0 -45
  173. package/dist/commands/export-site.js +0 -301
  174. package/dist/commands/export-site.js.map +0 -1
  175. package/src/commands/export-site.ts +0 -356
@@ -64,6 +64,9 @@ import { ReportResource } from '../resources/report-resource.js';
64
64
  import { TemplateResource } from '../resources/template-resource.js';
65
65
  import { WorkflowResource } from '../resources/workflow-resource.js';
66
66
 
67
+ import { ContentWatcher } from './project/project-content-watcher.js';
68
+ import { pathToResourceName } from '../utils/resource-utils.js';
69
+
67
70
  // Re-export this, so that classes that use Project do not need to have separate import.
68
71
  export { ResourcesFrom };
69
72
 
@@ -75,12 +78,16 @@ export class Project extends CardContainer {
75
78
  private projectPaths: ProjectPaths;
76
79
  private settings: ProjectConfiguration;
77
80
  private validator: Validate;
81
+ private resourceWatcher: ContentWatcher | undefined;
78
82
 
79
83
  // Created resources are held in a cache.
80
84
  // In the cache, key is resource name, and data is resource metadata (as JSON).
81
85
  private createdResources = new Map<string, JSON>();
82
86
 
83
- constructor(path: string) {
87
+ constructor(
88
+ path: string,
89
+ private watchResourceChanges?: boolean,
90
+ ) {
84
91
  super(path, '');
85
92
 
86
93
  this.settings = new ProjectConfiguration(
@@ -93,6 +100,48 @@ export class Project extends CardContainer {
93
100
  // todo: implement project validation
94
101
  this.validator = Validate.getInstance();
95
102
  this.resources.collectLocalResources();
103
+
104
+ const ignoreRenameFileChanges = true;
105
+
106
+ // Watch changes in .cards if there are multiple instances of Project being
107
+ // run concurrently.
108
+ if (this.watchResourceChanges) {
109
+ this.resourceWatcher = new ContentWatcher(
110
+ ignoreRenameFileChanges,
111
+ this.paths.resourcesFolder,
112
+ async (fileName: string) => {
113
+ let resource;
114
+ try {
115
+ resource = pathToResourceName(
116
+ this,
117
+ join(this.paths.resourcesFolder, fileName),
118
+ );
119
+ if (!resource) {
120
+ return;
121
+ }
122
+ } catch {
123
+ // it wasn't a resource that changed, so ignore the change
124
+ return;
125
+ }
126
+ const resourceName = `${resource.prefix}/${resource.type}/${resource.identifier}`;
127
+ await this.replaceCacheValue(resourceName);
128
+ this.resources.collectLocalResources();
129
+ },
130
+ );
131
+ }
132
+ }
133
+
134
+ // Removes current version of resource from cache.
135
+ // Then re-creates the resource with current data and caches the value again.
136
+ // If the value wasn't in the cache before, it will be added.
137
+ private async replaceCacheValue(resourceName: string) {
138
+ if (this.createdResources.has(resourceName)) {
139
+ // First, remove the old version from cache
140
+ this.createdResources.delete(resourceName);
141
+ }
142
+ const resourceData = await this.resource(resourceName);
143
+ if (!resourceData) return;
144
+ this.createdResources.set(resourceName, resourceData as JSON);
96
145
  }
97
146
 
98
147
  // Finds specific module.
@@ -318,6 +367,16 @@ export class Project extends CardContainer {
318
367
  return new TemplateResource(this, resourceName(template)).templateObject();
319
368
  }
320
369
 
370
+ /**
371
+ * Cleanups project when it is being closed.
372
+ */
373
+ public dispose() {
374
+ if (this.resourceWatcher) {
375
+ this.resourceWatcher.close();
376
+ this.resourceWatcher = undefined;
377
+ }
378
+ }
379
+
321
380
  /**
322
381
  * Returns an array of all the field types in the project.
323
382
  * @param from Defines where resources are collected from.
@@ -601,31 +660,58 @@ export class Project extends CardContainer {
601
660
  path: modulePath,
602
661
  cardKeyPrefix: moduleConfig.cardKeyPrefix,
603
662
  calculations: [
604
- ...(await this.resources.collectResourcesFromModules('calculations')),
663
+ ...(await this.resources.collectResourcesFromModules(
664
+ 'calculations',
665
+ moduleName,
666
+ )),
605
667
  ],
606
668
  cardTypes: [
607
- ...(await this.resources.collectResourcesFromModules('cardTypes')),
669
+ ...(await this.resources.collectResourcesFromModules(
670
+ 'cardTypes',
671
+ moduleName,
672
+ )),
608
673
  ],
609
674
  fieldTypes: [
610
- ...(await this.resources.collectResourcesFromModules('fieldTypes')),
675
+ ...(await this.resources.collectResourcesFromModules(
676
+ 'fieldTypes',
677
+ moduleName,
678
+ )),
611
679
  ],
612
680
  graphModels: [
613
- ...(await this.resources.collectResourcesFromModules('graphModels')),
681
+ ...(await this.resources.collectResourcesFromModules(
682
+ 'graphModels',
683
+ moduleName,
684
+ )),
614
685
  ],
615
686
  graphViews: [
616
- ...(await this.resources.collectResourcesFromModules('graphViews')),
687
+ ...(await this.resources.collectResourcesFromModules(
688
+ 'graphViews',
689
+ moduleName,
690
+ )),
617
691
  ],
618
692
  linkTypes: [
619
- ...(await this.resources.collectResourcesFromModules('linkTypes')),
693
+ ...(await this.resources.collectResourcesFromModules(
694
+ 'linkTypes',
695
+ moduleName,
696
+ )),
620
697
  ],
621
698
  reports: [
622
- ...(await this.resources.collectResourcesFromModules('reports')),
699
+ ...(await this.resources.collectResourcesFromModules(
700
+ 'reports',
701
+ moduleName,
702
+ )),
623
703
  ],
624
704
  templates: [
625
- ...(await this.resources.collectResourcesFromModules('templates')),
705
+ ...(await this.resources.collectResourcesFromModules(
706
+ 'templates',
707
+ moduleName,
708
+ )),
626
709
  ],
627
710
  workflows: [
628
- ...(await this.resources.collectResourcesFromModules('workflows')),
711
+ ...(await this.resources.collectResourcesFromModules(
712
+ 'workflows',
713
+ moduleName,
714
+ )),
629
715
  ],
630
716
  };
631
717
  }
@@ -764,15 +850,6 @@ export class Project extends CardContainer {
764
850
  return prefixes;
765
851
  }
766
852
 
767
- /**
768
- * Removes a module from project.
769
- * @param moduleName Name of the module
770
- */
771
- public async removeModule(moduleName: string) {
772
- await this.configuration.removeModule(moduleName);
773
- await this.collectModuleResources();
774
- }
775
-
776
853
  /**
777
854
  * Array of reports in the project.
778
855
  * @param from Defines where resources are collected from.
@@ -37,7 +37,7 @@ import {
37
37
  getRankAfter,
38
38
  sortItems,
39
39
  } from '../utils/lexorank.js';
40
- import { logger } from '../utils/log-utils.js';
40
+ import { getChildLogger } from '../utils/log-utils.js';
41
41
  import { readJsonFile } from '../utils/json.js';
42
42
  import { Project } from './project.js';
43
43
  import { resourceName } from '../utils/resource-utils.js';
@@ -47,6 +47,11 @@ export class Template extends CardContainer {
47
47
  private templatePath: string;
48
48
  private templateCardsPath: string;
49
49
  private project: Project;
50
+ private get logger() {
51
+ return getChildLogger({
52
+ module: 'template',
53
+ });
54
+ }
50
55
 
51
56
  constructor(project: Project, template: Resource) {
52
57
  // Templates might come from modules. Remove module name from template name.
@@ -465,7 +470,7 @@ export class Template extends CardContainer {
465
470
  details?: FetchCardDetails,
466
471
  ): Promise<Card[]> {
467
472
  if (placeHolderPath) {
468
- logger.warn('A non-used variable was used in the cards method');
473
+ this.logger.warn('A non-used variable was used in the cards method');
469
474
  }
470
475
  const cardDetails = details
471
476
  ? details
@@ -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/index.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  CommandManager,
18
18
  ExportFormats,
19
19
  } from './command-handler.js';
20
+ import type { Credentials } from './interfaces/project-interfaces.js';
20
21
  import { requestStatus } from './interfaces/request-status-interfaces.js';
21
22
  import { UpdateOperations } from './resources/resource-object.js';
22
23
  import { evaluateMacros } from './macros/index.js';
@@ -26,6 +27,7 @@ export {
26
27
  Cmd,
27
28
  CommandManager,
28
29
  Commands,
30
+ Credentials,
29
31
  ExportFormats,
30
32
  requestStatus,
31
33
  UpdateOperations,
@@ -11,11 +11,13 @@
11
11
  */
12
12
 
13
13
  import type { macroMetadata } from '../macros/common.js';
14
+ import type { Project } from '../containers/project.js';
15
+ import type { MacroError } from '../exceptions/index.js';
14
16
 
15
17
  type Mode = 'validate' | 'static' | 'inject';
16
18
 
17
19
  export interface MacroGenerationContext {
18
- projectPath: string;
20
+ project: Project;
19
21
  mode: Mode;
20
22
  cardKey: string;
21
23
  }
@@ -43,6 +45,8 @@ export interface MacroTaskState {
43
45
  placeholder: string;
44
46
  promiseResult: string | null;
45
47
  macro: string;
48
+ parameters: string;
49
+ error: MacroError | null;
46
50
  }
47
51
 
48
52
  // Handlebars options is not documented
@@ -65,6 +65,12 @@ export interface CardMetadata extends PredefinedCardMetadata {
65
65
  // Content in project (apart from cards) is either .schema files or cardsConfig.json.
66
66
  type ContentType = DotSchemaContent | ProjectSettings;
67
67
 
68
+ // Credentials for private modules.
69
+ export interface Credentials {
70
+ username?: string;
71
+ token?: string;
72
+ }
73
+
68
74
  // Single CSV row as read from a file.
69
75
  export type CSVRowRaw = {
70
76
  [key: string]: string;
@@ -147,9 +153,11 @@ export interface ModuleSetting extends ModuleSettingOptions {
147
153
  location: string;
148
154
  }
149
155
 
156
+ // Additional options for module configuration.
150
157
  export interface ModuleSettingOptions {
151
158
  branch?: string;
152
159
  private?: boolean;
160
+ credentials?: Credentials;
153
161
  }
154
162
 
155
163
  // Resources that are possible to remove.
@@ -1,13 +1,14 @@
1
1
  /**
2
- Cyberismo
3
- Copyright © Cyberismo Ltd and contributors 2024
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
-
7
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
-
9
- You should have received a copy of the GNU Affero General Public
10
- License along with this program. If not, see <https://www.gnu.org/licenses/>.
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
12
  */
12
13
 
13
14
  import type { Schema } from 'jsonschema';
@@ -30,6 +31,7 @@ export interface CardType extends ResourceBaseMetadata {
30
31
  }
31
32
 
32
33
  // Custom field
34
+ // todo: merge with FieldType.
33
35
  export interface CustomField {
34
36
  name: string;
35
37
  description?: string;
@@ -59,8 +61,6 @@ export interface EnumDefinition {
59
61
 
60
62
  // Field type content.
61
63
  export interface FieldType extends ResourceBaseMetadata {
62
- displayName?: string;
63
- fieldDescription?: string;
64
64
  dataType: DataType;
65
65
  enumValues?: Array<EnumDefinition>;
66
66
  }
@@ -68,8 +68,6 @@ export interface FieldType extends ResourceBaseMetadata {
68
68
  // Graph model content.
69
69
  export interface GraphModelMetadata extends ResourceBaseMetadata {
70
70
  category?: string;
71
- description?: string;
72
- displayName: string;
73
71
  }
74
72
 
75
73
  export interface GraphModel extends GraphModelMetadata {
@@ -79,8 +77,6 @@ export interface GraphModel extends GraphModelMetadata {
79
77
  // Graph view content.
80
78
  export interface GraphViewMetadata extends ResourceBaseMetadata {
81
79
  category?: string;
82
- description?: string;
83
- displayName: string;
84
80
  }
85
81
 
86
82
  export interface GraphView extends GraphViewMetadata {
@@ -104,7 +100,7 @@ export interface LinkType extends ResourceBaseMetadata {
104
100
  }
105
101
 
106
102
  // Report resource.
107
- export interface Report {
103
+ export interface Report extends ResourceBaseMetadata {
108
104
  name: string;
109
105
  metadata: ReportMetadata;
110
106
  contentTemplate: string;
@@ -114,14 +110,14 @@ export interface Report {
114
110
 
115
111
  // Metadata for report
116
112
  export interface ReportMetadata extends ResourceBaseMetadata {
117
- displayName: string;
118
- description: string;
119
113
  category: string;
120
114
  }
121
115
 
122
116
  // Base interface for all resources.
123
117
  export interface ResourceBaseMetadata {
124
118
  name: string;
119
+ description?: string;
120
+ displayName: string;
125
121
  usedIn?: string[];
126
122
  }
127
123
 
@@ -137,8 +133,7 @@ export type ResourceContent =
137
133
  | Workflow;
138
134
 
139
135
  // Template configuration details.
140
- export interface TemplateConfiguration {
141
- name: string;
136
+ export interface TemplateConfiguration extends ResourceBaseMetadata {
142
137
  path: string;
143
138
  numberOfCards: number;
144
139
  metadata: TemplateMetadata;
@@ -146,8 +141,6 @@ export interface TemplateConfiguration {
146
141
 
147
142
  // Template configuration content details.
148
143
  export interface TemplateMetadata extends ResourceBaseMetadata {
149
- displayName?: string;
150
- description?: string;
151
144
  category?: string;
152
145
  }
153
146
 
@@ -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 { handleMacroError } from './index.js';
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
- console.warn(
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.all(dependencies.map((dep) => dep.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
- let parsed;
123
- try {
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
- const task = this.tasks.find(this.globalId, localId);
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
- const task = this.tasks.find(this.globalId, localId);
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.promiseResult = handleMacroError(
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;
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  Cyberismo
3
3
  Copyright © Cyberismo Ltd and contributors 2024
4
-
5
4
  This program is free software: you can redistribute it and/or modify it under
6
5
  the terms of the GNU Affero General Public License version 3 as published by
7
6
  the Free Software Foundation. This program is distributed in the hope that it
@@ -18,16 +17,15 @@ import type { MacroOptions } from '../index.js';
18
17
  import { createImage, validateMacroContent } from '../index.js';
19
18
  import Handlebars from 'handlebars';
20
19
  import { join } from 'node:path';
21
- import { logger } from '../../utils/log-utils.js';
22
20
  import type { MacroGenerationContext } from '../../interfaces/macros.js';
23
21
  import macroMetadata from './metadata.js';
24
22
  import { pathExists } from '../../utils/file-utils.js';
25
- import { Project } from '../../containers/project.js';
26
23
  import { readFile } from 'node:fs/promises';
27
24
  import { resourceName } from '../../utils/resource-utils.js';
28
25
  import type { Schema } from 'jsonschema';
29
26
  import { validateJson } from '../../utils/validate.js';
30
27
  import type TaskQueue from '../task-queue.js';
28
+ import { ClingoError } from '@cyberismo/node-clingo';
31
29
 
32
30
  export interface GraphOptions extends MacroOptions {
33
31
  model: string;
@@ -48,16 +46,20 @@ class ReportMacro extends BaseMacro {
48
46
  };
49
47
 
50
48
  handleInject = async (context: MacroGenerationContext, input: unknown) => {
51
- const project = new Project(context.projectPath);
52
- const calculate = new Calculate(project);
49
+ const calculate = new Calculate(context.project);
53
50
 
54
51
  const resourceNameToPath = (name: string, fileName: string) => {
55
52
  const { identifier, prefix, type } = resourceName(name);
56
- if (prefix === project.projectPrefix) {
57
- return join(project.paths.resourcesFolder, type, identifier, fileName);
53
+ if (prefix === context.project.projectPrefix) {
54
+ return join(
55
+ context.project.paths.resourcesFolder,
56
+ type,
57
+ identifier,
58
+ fileName,
59
+ );
58
60
  }
59
61
  return join(
60
- project.paths.modulesFolder,
62
+ context.project.paths.modulesFolder,
61
63
  prefix,
62
64
  type,
63
65
  identifier,
@@ -76,7 +78,7 @@ class ReportMacro extends BaseMacro {
76
78
  ),
77
79
  );
78
80
  } catch (err) {
79
- logger.trace(
81
+ this.logger.trace(
80
82
  err,
81
83
  'Graph schema not found or failed to read, skipping validation',
82
84
  );
@@ -110,9 +112,17 @@ class ReportMacro extends BaseMacro {
110
112
  const view = handlebars.compile(viewContent)(handlebarsContext);
111
113
 
112
114
  const modelContent = await readFile(modelLocation, { encoding: 'utf-8' });
113
- const result = await calculate.runGraph({
114
- query: view + '\n' + modelContent,
115
- });
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
+ }
116
126
 
117
127
  if (typeof result !== 'string') {
118
128
  throw new Error(