@cyberismo/data-handler 0.0.2

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 (306) hide show
  1. package/LICENSE +702 -0
  2. package/dist/card-metadata-updater.d.ts +33 -0
  3. package/dist/card-metadata-updater.js +121 -0
  4. package/dist/card-metadata-updater.js.map +1 -0
  5. package/dist/command-handler.d.ts +96 -0
  6. package/dist/command-handler.js +557 -0
  7. package/dist/command-handler.js.map +1 -0
  8. package/dist/command-manager.d.ts +43 -0
  9. package/dist/command-manager.js +73 -0
  10. package/dist/command-manager.js.map +1 -0
  11. package/dist/commands/calculate.d.ts +86 -0
  12. package/dist/commands/calculate.js +444 -0
  13. package/dist/commands/calculate.js.map +1 -0
  14. package/dist/commands/create.d.ts +114 -0
  15. package/dist/commands/create.js +389 -0
  16. package/dist/commands/create.js.map +1 -0
  17. package/dist/commands/edit.d.ts +37 -0
  18. package/dist/commands/edit.js +99 -0
  19. package/dist/commands/edit.js.map +1 -0
  20. package/dist/commands/export-site.d.ts +45 -0
  21. package/dist/commands/export-site.js +301 -0
  22. package/dist/commands/export-site.js.map +1 -0
  23. package/dist/commands/export.d.ts +53 -0
  24. package/dist/commands/export.js +251 -0
  25. package/dist/commands/export.js.map +1 -0
  26. package/dist/commands/import.d.ts +53 -0
  27. package/dist/commands/import.js +133 -0
  28. package/dist/commands/import.js.map +1 -0
  29. package/dist/commands/index.d.ts +26 -0
  30. package/dist/commands/index.js +27 -0
  31. package/dist/commands/index.js.map +1 -0
  32. package/dist/commands/move.d.ts +55 -0
  33. package/dist/commands/move.js +341 -0
  34. package/dist/commands/move.js.map +1 -0
  35. package/dist/commands/remove.d.ts +38 -0
  36. package/dist/commands/remove.js +192 -0
  37. package/dist/commands/remove.js.map +1 -0
  38. package/dist/commands/rename.d.ts +46 -0
  39. package/dist/commands/rename.js +289 -0
  40. package/dist/commands/rename.js.map +1 -0
  41. package/dist/commands/show.d.ts +124 -0
  42. package/dist/commands/show.js +345 -0
  43. package/dist/commands/show.js.map +1 -0
  44. package/dist/commands/transition.d.ts +27 -0
  45. package/dist/commands/transition.js +92 -0
  46. package/dist/commands/transition.js.map +1 -0
  47. package/dist/commands/update.d.ts +29 -0
  48. package/dist/commands/update.js +64 -0
  49. package/dist/commands/update.js.map +1 -0
  50. package/dist/commands/validate.d.ts +143 -0
  51. package/dist/commands/validate.js +689 -0
  52. package/dist/commands/validate.js.map +1 -0
  53. package/dist/containers/card-container.d.ts +44 -0
  54. package/dist/containers/card-container.js +282 -0
  55. package/dist/containers/card-container.js.map +1 -0
  56. package/dist/containers/project/project-paths.d.ts +46 -0
  57. package/dist/containers/project/project-paths.js +105 -0
  58. package/dist/containers/project/project-paths.js.map +1 -0
  59. package/dist/containers/project/resource-collector.d.ts +86 -0
  60. package/dist/containers/project/resource-collector.js +331 -0
  61. package/dist/containers/project/resource-collector.js.map +1 -0
  62. package/dist/containers/project.d.ts +351 -0
  63. package/dist/containers/project.js +896 -0
  64. package/dist/containers/project.js.map +1 -0
  65. package/dist/containers/template.d.ts +108 -0
  66. package/dist/containers/template.js +433 -0
  67. package/dist/containers/template.js.map +1 -0
  68. package/dist/exceptions/index.d.ts +19 -0
  69. package/dist/exceptions/index.js +26 -0
  70. package/dist/exceptions/index.js.map +1 -0
  71. package/dist/index.d.ts +16 -0
  72. package/dist/index.js +15 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/interfaces/adoc.d.ts +12 -0
  75. package/dist/interfaces/adoc.js +13 -0
  76. package/dist/interfaces/adoc.js.map +1 -0
  77. package/dist/interfaces/macros.d.ts +45 -0
  78. package/dist/interfaces/macros.js +13 -0
  79. package/dist/interfaces/macros.js.map +1 -0
  80. package/dist/interfaces/project-interfaces.d.ts +121 -0
  81. package/dist/interfaces/project-interfaces.js +21 -0
  82. package/dist/interfaces/project-interfaces.js.map +1 -0
  83. package/dist/interfaces/request-status-interfaces.d.ts +28 -0
  84. package/dist/interfaces/request-status-interfaces.js +20 -0
  85. package/dist/interfaces/request-status-interfaces.js.map +1 -0
  86. package/dist/interfaces/resource-interfaces.d.ts +117 -0
  87. package/dist/interfaces/resource-interfaces.js +20 -0
  88. package/dist/interfaces/resource-interfaces.js.map +1 -0
  89. package/dist/macros/base-macro.d.ts +31 -0
  90. package/dist/macros/base-macro.js +126 -0
  91. package/dist/macros/base-macro.js.map +1 -0
  92. package/dist/macros/common.d.ts +17 -0
  93. package/dist/macros/common.js +23 -0
  94. package/dist/macros/common.js.map +1 -0
  95. package/dist/macros/createCards/index.d.ts +36 -0
  96. package/dist/macros/createCards/index.js +35 -0
  97. package/dist/macros/createCards/index.js.map +1 -0
  98. package/dist/macros/createCards/metadata.d.ts +14 -0
  99. package/dist/macros/createCards/metadata.js +18 -0
  100. package/dist/macros/createCards/metadata.js.map +1 -0
  101. package/dist/macros/graph/index.d.ts +29 -0
  102. package/dist/macros/graph/index.js +91 -0
  103. package/dist/macros/graph/index.js.map +1 -0
  104. package/dist/macros/graph/metadata.d.ts +14 -0
  105. package/dist/macros/graph/metadata.js +18 -0
  106. package/dist/macros/graph/metadata.js.map +1 -0
  107. package/dist/macros/index.d.ts +93 -0
  108. package/dist/macros/index.js +237 -0
  109. package/dist/macros/index.js.map +1 -0
  110. package/dist/macros/report/index.d.ts +26 -0
  111. package/dist/macros/report/index.js +70 -0
  112. package/dist/macros/report/index.js.map +1 -0
  113. package/dist/macros/report/metadata.d.ts +14 -0
  114. package/dist/macros/report/metadata.js +18 -0
  115. package/dist/macros/report/metadata.js.map +1 -0
  116. package/dist/macros/scoreCard/index.d.ts +30 -0
  117. package/dist/macros/scoreCard/index.js +38 -0
  118. package/dist/macros/scoreCard/index.js.map +1 -0
  119. package/dist/macros/scoreCard/metadata.d.ts +14 -0
  120. package/dist/macros/scoreCard/metadata.js +18 -0
  121. package/dist/macros/scoreCard/metadata.js.map +1 -0
  122. package/dist/macros/task-queue.d.ts +46 -0
  123. package/dist/macros/task-queue.js +69 -0
  124. package/dist/macros/task-queue.js.map +1 -0
  125. package/dist/module-manager.d.ts +62 -0
  126. package/dist/module-manager.js +350 -0
  127. package/dist/module-manager.js.map +1 -0
  128. package/dist/permissions/action-guard.d.ts +28 -0
  129. package/dist/permissions/action-guard.js +61 -0
  130. package/dist/permissions/action-guard.js.map +1 -0
  131. package/dist/project-settings.d.ts +42 -0
  132. package/dist/project-settings.js +120 -0
  133. package/dist/project-settings.js.map +1 -0
  134. package/dist/resources/array-handler.d.ts +28 -0
  135. package/dist/resources/array-handler.js +116 -0
  136. package/dist/resources/array-handler.js.map +1 -0
  137. package/dist/resources/card-type-resource.d.ts +72 -0
  138. package/dist/resources/card-type-resource.js +334 -0
  139. package/dist/resources/card-type-resource.js.map +1 -0
  140. package/dist/resources/create-defaults.d.ts +81 -0
  141. package/dist/resources/create-defaults.js +184 -0
  142. package/dist/resources/create-defaults.js.map +1 -0
  143. package/dist/resources/field-type-resource.d.ts +88 -0
  144. package/dist/resources/field-type-resource.js +411 -0
  145. package/dist/resources/field-type-resource.js.map +1 -0
  146. package/dist/resources/file-resource.d.ts +50 -0
  147. package/dist/resources/file-resource.js +301 -0
  148. package/dist/resources/file-resource.js.map +1 -0
  149. package/dist/resources/folder-resource.d.ts +66 -0
  150. package/dist/resources/folder-resource.js +100 -0
  151. package/dist/resources/folder-resource.js.map +1 -0
  152. package/dist/resources/graph-model-resource.d.ts +78 -0
  153. package/dist/resources/graph-model-resource.js +164 -0
  154. package/dist/resources/graph-model-resource.js.map +1 -0
  155. package/dist/resources/graph-view-resource.d.ts +78 -0
  156. package/dist/resources/graph-view-resource.js +163 -0
  157. package/dist/resources/graph-view-resource.js.map +1 -0
  158. package/dist/resources/link-type-resource.d.ts +62 -0
  159. package/dist/resources/link-type-resource.js +150 -0
  160. package/dist/resources/link-type-resource.js.map +1 -0
  161. package/dist/resources/report-resource.d.ts +77 -0
  162. package/dist/resources/report-resource.js +171 -0
  163. package/dist/resources/report-resource.js.map +1 -0
  164. package/dist/resources/resource-object.d.ts +108 -0
  165. package/dist/resources/resource-object.js +147 -0
  166. package/dist/resources/resource-object.js.map +1 -0
  167. package/dist/resources/template-resource.d.ts +82 -0
  168. package/dist/resources/template-resource.js +173 -0
  169. package/dist/resources/template-resource.js.map +1 -0
  170. package/dist/resources/workflow-resource.d.ts +67 -0
  171. package/dist/resources/workflow-resource.js +156 -0
  172. package/dist/resources/workflow-resource.js.map +1 -0
  173. package/dist/types/queries.d.ts +142 -0
  174. package/dist/types/queries.js +16 -0
  175. package/dist/types/queries.js.map +1 -0
  176. package/dist/utils/card-utils.d.ts +34 -0
  177. package/dist/utils/card-utils.js +78 -0
  178. package/dist/utils/card-utils.js.map +1 -0
  179. package/dist/utils/clingo-fact-builder.d.ts +58 -0
  180. package/dist/utils/clingo-fact-builder.js +126 -0
  181. package/dist/utils/clingo-fact-builder.js.map +1 -0
  182. package/dist/utils/clingo-facts.d.ts +97 -0
  183. package/dist/utils/clingo-facts.js +352 -0
  184. package/dist/utils/clingo-facts.js.map +1 -0
  185. package/dist/utils/clingo-parser.d.ts +59 -0
  186. package/dist/utils/clingo-parser.js +403 -0
  187. package/dist/utils/clingo-parser.js.map +1 -0
  188. package/dist/utils/clingo-program-builder.d.ts +39 -0
  189. package/dist/utils/clingo-program-builder.js +57 -0
  190. package/dist/utils/clingo-program-builder.js.map +1 -0
  191. package/dist/utils/common-utils.d.ts +24 -0
  192. package/dist/utils/common-utils.js +47 -0
  193. package/dist/utils/common-utils.js.map +1 -0
  194. package/dist/utils/constants.d.ts +18 -0
  195. package/dist/utils/constants.js +27 -0
  196. package/dist/utils/constants.js.map +1 -0
  197. package/dist/utils/csv.d.ts +18 -0
  198. package/dist/utils/csv.js +45 -0
  199. package/dist/utils/csv.js.map +1 -0
  200. package/dist/utils/file-utils.d.ts +69 -0
  201. package/dist/utils/file-utils.js +158 -0
  202. package/dist/utils/file-utils.js.map +1 -0
  203. package/dist/utils/json.d.ts +61 -0
  204. package/dist/utils/json.js +108 -0
  205. package/dist/utils/json.js.map +1 -0
  206. package/dist/utils/lexorank.d.ts +59 -0
  207. package/dist/utils/lexorank.js +159 -0
  208. package/dist/utils/lexorank.js.map +1 -0
  209. package/dist/utils/log-utils.d.ts +40 -0
  210. package/dist/utils/log-utils.js +109 -0
  211. package/dist/utils/log-utils.js.map +1 -0
  212. package/dist/utils/random.d.ts +19 -0
  213. package/dist/utils/random.js +34 -0
  214. package/dist/utils/random.js.map +1 -0
  215. package/dist/utils/resource-utils.d.ts +45 -0
  216. package/dist/utils/resource-utils.js +137 -0
  217. package/dist/utils/resource-utils.js.map +1 -0
  218. package/dist/utils/sanitize-svg.d.ts +18 -0
  219. package/dist/utils/sanitize-svg.js +38 -0
  220. package/dist/utils/sanitize-svg.js.map +1 -0
  221. package/dist/utils/user-preferences.d.ts +64 -0
  222. package/dist/utils/user-preferences.js +106 -0
  223. package/dist/utils/user-preferences.js.map +1 -0
  224. package/dist/utils/validate.d.ts +26 -0
  225. package/dist/utils/validate.js +53 -0
  226. package/dist/utils/validate.js.map +1 -0
  227. package/dist/utils/value-utils.d.ts +58 -0
  228. package/dist/utils/value-utils.js +181 -0
  229. package/dist/utils/value-utils.js.map +1 -0
  230. package/package.json +67 -0
  231. package/src/card-metadata-updater.ts +182 -0
  232. package/src/command-handler.ts +686 -0
  233. package/src/command-manager.ts +99 -0
  234. package/src/commands/calculate.ts +591 -0
  235. package/src/commands/create.ts +559 -0
  236. package/src/commands/edit.ts +123 -0
  237. package/src/commands/export-site.ts +356 -0
  238. package/src/commands/export.ts +315 -0
  239. package/src/commands/import.ts +169 -0
  240. package/src/commands/index.ts +42 -0
  241. package/src/commands/move.ts +451 -0
  242. package/src/commands/remove.ts +244 -0
  243. package/src/commands/rename.ts +378 -0
  244. package/src/commands/show.ts +442 -0
  245. package/src/commands/transition.ts +127 -0
  246. package/src/commands/update.ts +76 -0
  247. package/src/commands/validate.ts +962 -0
  248. package/src/containers/card-container.ts +378 -0
  249. package/src/containers/project/project-paths.ts +127 -0
  250. package/src/containers/project/resource-collector.ts +379 -0
  251. package/src/containers/project.ts +1135 -0
  252. package/src/containers/template.ts +573 -0
  253. package/src/exceptions/index.ts +29 -0
  254. package/src/index.ts +33 -0
  255. package/src/interfaces/adoc.ts +18 -0
  256. package/src/interfaces/macros.ts +54 -0
  257. package/src/interfaces/project-interfaces.ts +208 -0
  258. package/src/interfaces/request-status-interfaces.ts +30 -0
  259. package/src/interfaces/resource-interfaces.ts +179 -0
  260. package/src/macros/base-macro.ts +176 -0
  261. package/src/macros/common.ts +24 -0
  262. package/src/macros/createCards/index.ts +57 -0
  263. package/src/macros/createCards/metadata.ts +21 -0
  264. package/src/macros/graph/index.ts +130 -0
  265. package/src/macros/graph/metadata.ts +21 -0
  266. package/src/macros/index.ts +321 -0
  267. package/src/macros/report/index.ts +88 -0
  268. package/src/macros/report/metadata.ts +21 -0
  269. package/src/macros/scoreCard/index.ts +55 -0
  270. package/src/macros/scoreCard/metadata.ts +21 -0
  271. package/src/macros/task-queue.ts +79 -0
  272. package/src/module-manager.ts +443 -0
  273. package/src/permissions/action-guard.ts +77 -0
  274. package/src/project-settings.ts +140 -0
  275. package/src/resources/array-handler.ts +141 -0
  276. package/src/resources/card-type-resource.ts +455 -0
  277. package/src/resources/create-defaults.ts +216 -0
  278. package/src/resources/field-type-resource.ts +533 -0
  279. package/src/resources/file-resource.ts +433 -0
  280. package/src/resources/folder-resource.ts +140 -0
  281. package/src/resources/graph-model-resource.ts +205 -0
  282. package/src/resources/graph-view-resource.ts +199 -0
  283. package/src/resources/link-type-resource.ts +191 -0
  284. package/src/resources/report-resource.ts +224 -0
  285. package/src/resources/resource-object.ts +246 -0
  286. package/src/resources/template-resource.ts +210 -0
  287. package/src/resources/workflow-resource.ts +205 -0
  288. package/src/types/queries.ts +149 -0
  289. package/src/utils/card-utils.ts +83 -0
  290. package/src/utils/clingo-fact-builder.ts +167 -0
  291. package/src/utils/clingo-facts.ts +550 -0
  292. package/src/utils/clingo-parser.ts +519 -0
  293. package/src/utils/clingo-program-builder.ts +71 -0
  294. package/src/utils/common-utils.ts +54 -0
  295. package/src/utils/constants.ts +32 -0
  296. package/src/utils/csv.ts +53 -0
  297. package/src/utils/file-utils.ts +182 -0
  298. package/src/utils/json.ts +118 -0
  299. package/src/utils/lexorank.ts +180 -0
  300. package/src/utils/log-utils.ts +127 -0
  301. package/src/utils/random.ts +37 -0
  302. package/src/utils/resource-utils.ts +180 -0
  303. package/src/utils/sanitize-svg.ts +46 -0
  304. package/src/utils/user-preferences.ts +126 -0
  305. package/src/utils/validate.ts +66 -0
  306. package/src/utils/value-utils.ts +189 -0
@@ -0,0 +1,442 @@
1
+ /**
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/>.
12
+ */
13
+
14
+ // node
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { homedir } from 'node:os';
17
+ import { join, resolve } from 'node:path';
18
+ import { spawn } from 'node:child_process';
19
+ import { writeFile } from 'node:fs/promises';
20
+
21
+ import mime from 'mime-types';
22
+
23
+ import type { attachmentPayload } from '../interfaces/request-status-interfaces.js';
24
+ import type {
25
+ CardAttachment,
26
+ Card,
27
+ CardListContainer,
28
+ ModuleContent,
29
+ ProjectFetchCardDetails,
30
+ ProjectMetadata,
31
+ Resource,
32
+ } from '../interfaces/project-interfaces.js';
33
+ import type {
34
+ CardType,
35
+ ResourceContent,
36
+ TemplateConfiguration,
37
+ Workflow,
38
+ } from '../interfaces/resource-interfaces.js';
39
+ import { Project, type ResourcesFrom } from '../containers/project.js';
40
+ import { resourceName } from '../utils/resource-utils.js';
41
+ import { TemplateResource } from '../resources/template-resource.js';
42
+ import { UserPreferences } from '../utils/user-preferences.js';
43
+
44
+ import ReportMacro from '../macros/report/index.js';
45
+ import TaskQueue from '../macros/task-queue.js';
46
+
47
+ /**
48
+ * Show command.
49
+ */
50
+ export class Show {
51
+ private resourceFunction: Map<
52
+ string,
53
+ (from?: ResourcesFrom) => Promise<Resource[]>
54
+ >;
55
+ constructor(private project: Project) {
56
+ this.resourceFunction = new Map([
57
+ ['cardTypes', this.project.cardTypes.bind(this.project)],
58
+ ['fieldTypes', this.project.fieldTypes.bind(this.project)],
59
+ ['graphModels', this.project.graphModels.bind(this.project)],
60
+ ['graphViews', this.project.graphViews.bind(this.project)],
61
+ ['linkTypes', this.project.linkTypes.bind(this.project)],
62
+ ['reports', this.project.reports.bind(this.project)],
63
+ ['templates', this.project.templates.bind(this.project)],
64
+ ['workflows', this.project.workflows.bind(this.project)],
65
+ ]);
66
+ }
67
+
68
+ // Collect all labels from cards.
69
+ private collectLabels = (cards: Card[]): string[] => {
70
+ return cards.reduce<string[]>((labels, card) => {
71
+ // Add the labels from the current card
72
+ if (card.metadata?.labels) {
73
+ labels.push(...card.metadata.labels);
74
+ }
75
+ // Recursively collect labels from subcards, if they exist
76
+ if (card.children) {
77
+ labels.push(...this.collectLabels(card.children));
78
+ }
79
+ return labels;
80
+ }, []);
81
+ };
82
+
83
+ // Returns attachment details
84
+ private async getAttachment(cardKey: string, filename: string) {
85
+ const details = {
86
+ content: false,
87
+ metadata: true,
88
+ children: false,
89
+ parent: false,
90
+ attachments: true,
91
+ };
92
+ const card = await this.project.cardDetailsById(cardKey, details);
93
+ if (card === undefined) {
94
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
95
+ }
96
+
97
+ const attachment =
98
+ card.attachments?.find((a) => a.fileName === filename) ?? undefined;
99
+ return attachment;
100
+ }
101
+
102
+ // Opens the given path using the operating system's default application. Doesn't block the main thread.
103
+ // @todo: Move away from Show.
104
+ private openUsingDefaultApplication(path: string) {
105
+ if (process.platform === 'win32') {
106
+ // This is a workaround to get windows to open the file in foreground
107
+ spawn(`start`, ['cmd.exe', '/c', 'start', '""', `"${path}"`], {
108
+ shell: true,
109
+ });
110
+ } else if (process.platform === 'darwin') {
111
+ spawn('open', [path]);
112
+ } else {
113
+ spawn('xdg-open', [path]);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Shows all attachments (either template or project attachments) from a project.
119
+ * @returns array of card attachments
120
+ */
121
+ public async showAttachments(): Promise<CardAttachment[]> {
122
+ const attachments: CardAttachment[] = await this.project.attachments();
123
+ const templateAttachments: CardAttachment[] = [];
124
+ const templates = await this.project.templates();
125
+ for (const template of templates) {
126
+ const templateResource = new TemplateResource(
127
+ this.project,
128
+ resourceName(template.name),
129
+ );
130
+ const templateObject = templateResource.templateObject();
131
+ if (templateObject) {
132
+ templateAttachments.push(...(await templateObject.attachments()));
133
+ }
134
+ }
135
+
136
+ attachments.push(...templateAttachments);
137
+ return attachments;
138
+ }
139
+
140
+ /**
141
+ * Returns file buffer and mime type of an attachment. Used by app UI to download attachments.
142
+ * @param cardKey card key to find
143
+ * @param filename attachment filename
144
+ * @returns attachment details
145
+ */
146
+ public async showAttachment(
147
+ cardKey: string,
148
+ filename: string,
149
+ ): Promise<attachmentPayload> {
150
+ if (!cardKey) {
151
+ throw new Error(`Mandatory parameter 'cardKey' missing`);
152
+ }
153
+
154
+ const attachment = await this.getAttachment(cardKey, filename);
155
+
156
+ let attachmentPath: string = '';
157
+ if (attachment) {
158
+ attachmentPath = `${attachment.path}/${attachment.fileName}`;
159
+ }
160
+
161
+ if (!attachment || !existsSync(attachmentPath)) {
162
+ throw new Error(`Attachment '${filename}' not found for card ${cardKey}`);
163
+ } else {
164
+ const fileBuffer = readFileSync(attachmentPath);
165
+ let mimeType = mime.lookup(attachmentPath);
166
+ if (mimeType === false) {
167
+ mimeType = 'application/octet-stream';
168
+ }
169
+ const payload: attachmentPayload = { fileBuffer, mimeType };
170
+ return payload;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Opens an attachment using a configured application or the operating system's default application.
176
+ * @param cardKey card key of the attachment
177
+ * @param filename attachment filename
178
+ * @param waitDelay amount of time to wait for the application to open the attachment
179
+ * @todo: Move away from Show.
180
+ */
181
+ public async openAttachment(
182
+ cardKey: string,
183
+ filename: string,
184
+ waitDelay: number = 1000,
185
+ ) {
186
+ const attachment = await this.getAttachment(cardKey, filename);
187
+
188
+ if (!attachment) {
189
+ throw new Error(`Attachment '${filename}' not found for card ${cardKey}`);
190
+ }
191
+
192
+ // Try to open the attachment using a configured application if one exists
193
+ const prefs = new UserPreferences(
194
+ join(homedir(), '.cyberismo', 'cards.prefs.json'),
195
+ ).getPreferences();
196
+ const attachmentEditors =
197
+ prefs.attachmentEditors && process.platform in prefs.attachmentEditors
198
+ ? prefs.attachmentEditors[process.platform]
199
+ : [];
200
+
201
+ const editor = attachmentEditors.find(
202
+ (editor) => editor.mimeType === attachment.mimeType,
203
+ );
204
+
205
+ const path = resolve(attachment.path, attachment.fileName);
206
+
207
+ if (!editor) {
208
+ this.openUsingDefaultApplication(path);
209
+ return;
210
+ }
211
+
212
+ // We can safely assume that the editor command is safe to execute, since it is defined in the preferences file by the user
213
+ const processHandle = spawn(
214
+ editor.command.replace('{{attachmentPath}}', path),
215
+ [],
216
+ {
217
+ shell: true,
218
+ },
219
+ );
220
+
221
+ // wait for the application to open the attachment
222
+ await new Promise((resolve) => setTimeout(resolve, waitDelay));
223
+
224
+ // If the application exists with a non-zero exit code, open the attachment using the operating system's default application
225
+ if (processHandle.exitCode !== 0 && processHandle.exitCode !== null) {
226
+ this.openUsingDefaultApplication(path);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Shows details of a particular card (template card, or project card)
232
+ * @note Note that parameter 'cardKey' is optional due to technical limitations of class calling this class. It must be defined to get valid results.
233
+ * @param details card details to show
234
+ * @param cardKey card key to find
235
+ * @returns card details
236
+ */
237
+ public async showCardDetails(
238
+ details: ProjectFetchCardDetails,
239
+ cardKey?: string,
240
+ ): Promise<Card> {
241
+ if (!cardKey) {
242
+ throw new Error(`Mandatory parameter 'cardKey' missing`);
243
+ }
244
+ const cardDetails = await this.project.cardDetailsById(cardKey, details);
245
+ if (cardDetails === undefined) {
246
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
247
+ }
248
+ return cardDetails;
249
+ }
250
+
251
+ /**
252
+ * Shows all cards (either template or project cards) from a project.
253
+ * @returns cards list array
254
+ */
255
+ public async showCards(): Promise<CardListContainer[]> {
256
+ return this.project.listCards();
257
+ }
258
+
259
+ /**
260
+ * Shows all card types in a project.
261
+ * @returns array of card type details
262
+ */
263
+ public async showCardTypesWithDetails(): Promise<(CardType | undefined)[]> {
264
+ const promiseContainer = [];
265
+ for (const cardType of await this.project.cardTypes()) {
266
+ const cardTypeDetails = await this.project.resource<CardType>(
267
+ cardType.name,
268
+ );
269
+ if (cardTypeDetails) {
270
+ promiseContainer.push(cardTypeDetails);
271
+ }
272
+ }
273
+ const results = await Promise.all(promiseContainer);
274
+ return results.filter((item) => item);
275
+ }
276
+
277
+ /**
278
+ * Returns all unique labels in a project
279
+ * @returns labels in a list
280
+ */
281
+ public async showLabels(): Promise<string[]> {
282
+ const cards = await this.project.showProjectCards();
283
+ const templateCards = await this.project.allTemplateCards({
284
+ metadata: true,
285
+ children: true,
286
+ });
287
+
288
+ const labels = this.collectLabels([...cards, ...templateCards]);
289
+ return Array.from(new Set(labels));
290
+ }
291
+
292
+ /**
293
+ * Shows details of a module.
294
+ * @param moduleName name of a module
295
+ * @returns details of a module.
296
+ */
297
+ public async showModule(moduleName: string): Promise<ModuleContent> {
298
+ const moduleDetails = await this.project.module(moduleName);
299
+ if (!moduleDetails) {
300
+ throw new Error(`Module '${moduleName}' does not exist in the project`);
301
+ }
302
+ return moduleDetails;
303
+ }
304
+
305
+ /**
306
+ * Returns all project cards in the project. Cards don't have content and nor metadata.
307
+ * @note AppUi uses this method.
308
+ * @returns array of cards
309
+ */
310
+ public async showProjectCards(): Promise<Card[]> {
311
+ const projectCards = await this.project.showProjectCards();
312
+ return projectCards;
313
+ }
314
+
315
+ /**
316
+ * Shows all modules (if any) in a project.
317
+ * @returns all modules in a project.
318
+ */
319
+ public async showModules(): Promise<string[]> {
320
+ return (await this.project.modules()).map((item) => item.name).sort();
321
+ }
322
+
323
+ /**
324
+ * Shows details of a particular project.
325
+ * @returns project information
326
+ */
327
+ public async showProject(): Promise<ProjectMetadata> {
328
+ return this.project.show();
329
+ }
330
+
331
+ /**
332
+ * Shows report results for a given report name and card key.
333
+ * @param reportName Name of the report to show
334
+ * @param cardKey Card key to use for the report
335
+ * @param parameters Additional parameters for the report
336
+ * @param outputPath Optional output path for the report
337
+ * @returns Report results as a string
338
+ * @throws Error if the report does not exist
339
+ */
340
+ public async showReportResults(
341
+ reportName: string,
342
+ cardKey: string,
343
+ parameters: object,
344
+ outputPath?: string,
345
+ ): Promise<string> {
346
+ if (
347
+ !(await this.project.reports()).some(
348
+ (report) => report.name === reportName,
349
+ )
350
+ ) {
351
+ throw new Error(`Report '${reportName}' does not exist`);
352
+ }
353
+
354
+ const reportMacro = new ReportMacro(new TaskQueue());
355
+ const result = await reportMacro.handleInject(
356
+ {
357
+ projectPath: this.project.basePath,
358
+ cardKey: cardKey,
359
+ mode: 'static',
360
+ },
361
+ { name: reportName, ...parameters },
362
+ );
363
+
364
+ // Show the results either in the console or write to a file.
365
+ if (outputPath) {
366
+ try {
367
+ await writeFile(outputPath, result ?? '', 'utf-8');
368
+ } catch (error) {
369
+ if (error instanceof Error) {
370
+ throw new Error(
371
+ `Failed to write report to ${outputPath}: ${error.message}`,
372
+ );
373
+ }
374
+ }
375
+ }
376
+ return outputPath ? '' : (result ?? '');
377
+ }
378
+
379
+ /**
380
+ * Shows details of certain resource.
381
+ * @param name Name of resource.
382
+ * @returns resource metadata as JSON.
383
+ */
384
+ public async showResource(
385
+ name: string,
386
+ showUse: boolean = false,
387
+ ): Promise<ResourceContent | undefined> {
388
+ const strictNameCheck = true;
389
+ const resource = Project.resourceObject(
390
+ this.project,
391
+ resourceName(name, strictNameCheck),
392
+ );
393
+ const [details, usage] = await Promise.all([
394
+ resource?.show(),
395
+ showUse ? resource?.usage() : [],
396
+ ]);
397
+ if (showUse) {
398
+ return {
399
+ ...details,
400
+ usedIn: [...usage],
401
+ };
402
+ } else {
403
+ return details;
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Shows all available resources of a given type.
409
+ * @param type Name of resources to return (in plural form, e.g. 'templates')
410
+ * @returns sorted array of resources
411
+ */
412
+ public async showResources(type: string): Promise<string[]> {
413
+ const func = this.resourceFunction.get(type);
414
+ if (!func) return [];
415
+ return (await func()).map((item) => item.name).sort();
416
+ }
417
+
418
+ /**
419
+ * Shows all templates with full details in a project.
420
+ * @returns all templates in a project.
421
+ */
422
+ public async showTemplatesWithDetails(): Promise<TemplateConfiguration[]> {
423
+ const promiseContainer = (await this.project.templates()).map((template) =>
424
+ new TemplateResource(this.project, resourceName(template.name)).show(),
425
+ );
426
+ const result = await Promise.all(promiseContainer);
427
+ return result.filter(Boolean) as TemplateConfiguration[];
428
+ }
429
+
430
+ /**
431
+ * Shows all workflows with full details in a project.
432
+ * @returns workflows with full details
433
+ */
434
+ public async showWorkflowsWithDetails(): Promise<(Workflow | undefined)[]> {
435
+ const promiseContainer = [];
436
+ for (const workflow of await this.project.workflows()) {
437
+ promiseContainer.push(this.project.resource<Workflow>(workflow.name));
438
+ }
439
+ const results = await Promise.all(promiseContainer);
440
+ return results.filter((item) => item);
441
+ }
442
+ }
@@ -0,0 +1,127 @@
1
+ /**
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/>.
12
+ */
13
+
14
+ import { ActionGuard } from '../permissions/action-guard.js';
15
+ import type { Calculate } from './calculate.js';
16
+ import type {
17
+ CardType,
18
+ Workflow,
19
+ WorkflowState,
20
+ } from '../interfaces/resource-interfaces.js';
21
+ import { CardMetadataUpdater } from '../card-metadata-updater.js';
22
+ import type { Project } from '../containers/project.js';
23
+
24
+ export class Transition {
25
+ constructor(
26
+ private project: Project,
27
+ private calculateCmd: Calculate,
28
+ ) {}
29
+
30
+ /**
31
+ * Transitions a card from its current state to a new state.
32
+ * @param cardKey card key
33
+ * @param transition which transition to do
34
+ */
35
+ public async cardTransition(cardKey: string, transition: WorkflowState) {
36
+ // Card details
37
+ const details = await this.project.cardDetailsById(cardKey, {
38
+ metadata: true,
39
+ });
40
+ if (!details || !details.metadata) {
41
+ throw new Error(`Card ${cardKey} does not exist in the project`);
42
+ }
43
+
44
+ // Card type
45
+ const cardType = await this.project.resource<CardType>(
46
+ details.metadata?.cardType,
47
+ );
48
+ if (cardType === undefined) {
49
+ throw new Error(
50
+ `Card's card type '${details.metadata?.cardType}' does not exist in the project`,
51
+ );
52
+ }
53
+
54
+ // Workflow
55
+ const workflow = await this.project.resource<Workflow>(cardType.workflow);
56
+ if (workflow === undefined) {
57
+ throw new Error(
58
+ `Card's workflow '${cardType.workflow}' does not exist in the project`,
59
+ );
60
+ }
61
+
62
+ // Check that the state transition can be made "from".
63
+ const foundFrom = workflow.transitions.find(
64
+ (item) =>
65
+ (details.metadata &&
66
+ item.fromState.includes(details.metadata?.workflowState)) ||
67
+ item.fromState.includes('*'),
68
+ );
69
+ if (!foundFrom) {
70
+ throw new Error(
71
+ `Card's workflow '${cardType.workflow}' does not contain transition from card's current state '${details.metadata?.workflowState}'`,
72
+ );
73
+ }
74
+
75
+ // Check that the state transition can be made "to".
76
+ const found = workflow.transitions.find(
77
+ (item) => item.name === transition.name,
78
+ );
79
+ if (!found) {
80
+ const transitionNames = workflow.transitions.map((item) => item.name);
81
+ throw new Error(`Card's workflow '${cardType.workflow}' does not contain state transition '${transition.name}'.
82
+ \nThe available transitions are: ${transitionNames.join(', ')}`);
83
+ }
84
+
85
+ if (
86
+ !(
87
+ found.fromState.includes(details.metadata?.workflowState) ||
88
+ found.fromState.includes('*')
89
+ )
90
+ ) {
91
+ throw new Error(
92
+ `Card's workflow '${cardType.workflow}' does not contain state transition from state '${details.metadata?.workflowState}' for '${transition.name}`,
93
+ );
94
+ }
95
+
96
+ const actionGuard = new ActionGuard(this.calculateCmd);
97
+ await actionGuard.checkPermission('transition', cardKey, transition.name);
98
+
99
+ details.metadata.workflowState = found.toState;
100
+ details.metadata.lastUpdated = new Date().toISOString();
101
+ details.metadata.lastTransitioned = new Date().toISOString();
102
+ return this.project
103
+ .updateCardMetadata(details, details.metadata)
104
+ .then(async () => this.transitionChangesQuery(cardKey, transition.name))
105
+ .then(async (queryResult) => {
106
+ if (
107
+ !queryResult ||
108
+ queryResult.at(0) === undefined ||
109
+ queryResult.at(0)?.updateFields === undefined
110
+ ) {
111
+ return;
112
+ }
113
+ const fieldsToUpdate = queryResult.at(0)!.updateFields;
114
+ return CardMetadataUpdater.apply(this.project, fieldsToUpdate);
115
+ })
116
+ .catch((error) => console.error(error));
117
+ }
118
+
119
+ // Wrapper to run onTransition query.
120
+ private async transitionChangesQuery(cardKey: string, transition: string) {
121
+ if (!cardKey || !transition) return undefined;
122
+ return this.calculateCmd.runQuery('onTransition', {
123
+ cardKey,
124
+ transition,
125
+ });
126
+ }
127
+ }
@@ -0,0 +1,76 @@
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/>.
11
+ */
12
+
13
+ import type {
14
+ AddOperation,
15
+ ChangeOperation,
16
+ Operation,
17
+ RankOperation,
18
+ RemoveOperation,
19
+ UpdateOperations,
20
+ } from '../resources/resource-object.js';
21
+ import { Project } from '../containers/project.js';
22
+ import { resourceName } from '../utils/resource-utils.js';
23
+
24
+ /**
25
+ * Class that handles 'update' commands.
26
+ */
27
+ export class Update {
28
+ constructor(private project: Project) {}
29
+
30
+ /**
31
+ * Updates single resource property.
32
+ * @param name Name of the resource to operate on.
33
+ * @param operation Operation to perform ('add', 'remove', 'change', 'rank')
34
+ * @param key Property to change in resource JSON
35
+ * @param value Value for 'key'
36
+ * @param optionalDetail Additional detail needed for some operations. For example, 'update' needs a new value.
37
+ */
38
+ public async updateValue<Type>(
39
+ name: string,
40
+ operation: UpdateOperations,
41
+ key: string,
42
+ value: Type,
43
+ optionalDetail?: Type, // todo: for 'rank' it might be reasonable to accept also 'number'
44
+ ) {
45
+ const resource = Project.resourceObject(this.project, resourceName(name));
46
+ const op: Operation<Type> = {
47
+ name: operation,
48
+ target: '' as Type,
49
+ to: '' as Type,
50
+ newIndex: 0 as number,
51
+ };
52
+
53
+ // Set operation specific properties.
54
+ if (operation === 'add') {
55
+ (op as AddOperation<Type>).target = value;
56
+ } else if (operation === 'change') {
57
+ (op as ChangeOperation<Type>).target = optionalDetail
58
+ ? value
59
+ : (optionalDetail as Type);
60
+ (op as ChangeOperation<Type>).to = optionalDetail
61
+ ? optionalDetail
62
+ : (value as Type);
63
+ } else if (operation === 'rank') {
64
+ (op as RankOperation<Type>).newIndex = optionalDetail as number;
65
+ (op as RankOperation<Type>).target = value;
66
+ } else if (operation === 'remove') {
67
+ (op as RemoveOperation<Type>).target = value;
68
+ (op as RemoveOperation<Type>).replacementValue = optionalDetail
69
+ ? optionalDetail
70
+ : undefined;
71
+ }
72
+
73
+ await resource?.update(key, op);
74
+ this.project.collectLocalResources();
75
+ }
76
+ }