@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,559 @@
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 { basename, join, resolve, sep } from 'node:path';
16
+ import {
17
+ constants as fsConstants,
18
+ copyFile,
19
+ mkdir,
20
+ writeFile,
21
+ } from 'node:fs/promises';
22
+
23
+ import { type Calculate, Validate } from './index.js';
24
+ import { errorFunction } from '../utils/log-utils.js';
25
+ import { Project } from '../containers/project.js';
26
+ import { EMPTY_RANK, sortItems } from '../utils/lexorank.js';
27
+ import type {
28
+ DataType,
29
+ Link,
30
+ LinkType,
31
+ } from '../interfaces/resource-interfaces.js';
32
+ import { pathExists } from '../utils/file-utils.js';
33
+ import type { Card, ProjectFile } from '../interfaces/project-interfaces.js';
34
+ import { resourceName, resourceNameToString } from '../utils/resource-utils.js';
35
+ import { writeJsonFile } from '../utils/json.js';
36
+
37
+ import { CardTypeResource } from '../resources/card-type-resource.js';
38
+ import { FieldTypeResource } from '../resources/field-type-resource.js';
39
+ import { GraphModelResource } from '../resources/graph-model-resource.js';
40
+ import { GraphViewResource } from '../resources/graph-view-resource.js';
41
+ import { LinkTypeResource } from '../resources/link-type-resource.js';
42
+ import { ReportResource } from '../resources/report-resource.js';
43
+ import { TemplateResource } from '../resources/template-resource.js';
44
+ import { WorkflowResource } from '../resources/workflow-resource.js';
45
+
46
+ // todo: Is there a easy to way to make JSON schema into a TypeScript interface/type?
47
+ // Check this out: https://www.npmjs.com/package/json-schema-to-ts
48
+
49
+ /**
50
+ * Handles all create commands.
51
+ */
52
+ export class Create {
53
+ constructor(
54
+ private project: Project,
55
+ private calculateCmd: Calculate,
56
+ ) {}
57
+
58
+ static JSONFileContent: ProjectFile[] = [
59
+ {
60
+ path: '.cards/local',
61
+ content: [{ id: 'cardsConfigSchema', version: 1 }],
62
+ name: Project.schemaContentFile,
63
+ },
64
+ {
65
+ path: '.cards/local',
66
+ content: {
67
+ cardKeyPrefix: '$PROJECT-PREFIX',
68
+ name: '$PROJECT-NAME',
69
+ modules: [],
70
+ },
71
+ name: Project.projectConfigFileName,
72
+ },
73
+ ];
74
+
75
+ static gitIgnoreContent: string = `.calc\n
76
+ .asciidoctor\n
77
+ .vscode\n
78
+ *.html\n
79
+ *.pdf\n
80
+ *.puml\n
81
+ **/.DS_Store\n
82
+ *-debug.log\n
83
+ *-error.log\n`;
84
+
85
+ /**
86
+ * Adds new cards to a template.
87
+ * @param cardTypeName Card type for new cards.
88
+ * @param templateName Template name to add cards into.
89
+ * @param card Optional, if defined adds a new child-card under the card.
90
+ * @param count How many cards to add. By default one.
91
+ * @returns non-empty string array with ids of added cards
92
+ */
93
+ public async addCards(
94
+ cardTypeName: string,
95
+ templateName: string,
96
+ card?: string,
97
+ count: number = 1,
98
+ ): Promise<string[]> {
99
+ if (
100
+ !templateName ||
101
+ !Validate.validateFolder(join(this.project.basePath, templateName))
102
+ ) {
103
+ throw new Error(
104
+ `Input validation error: template name is invalid '${templateName}'`,
105
+ );
106
+ }
107
+ if (cardTypeName === undefined) {
108
+ throw new Error(`Input validation error: card type cannot be empty`);
109
+ }
110
+ // Use slice to get a copy of a string.
111
+ const origTemplateName = templateName.slice(0);
112
+ const templateResource = new TemplateResource(
113
+ this.project,
114
+ resourceName(templateName),
115
+ );
116
+ const templateObject = templateResource.templateObject();
117
+
118
+ const specificCard = card
119
+ ? await templateObject.findSpecificCard(card)
120
+ : undefined;
121
+ if (card && !specificCard) {
122
+ throw Error(
123
+ `Card '${card}' was not found from template '${origTemplateName}'`,
124
+ );
125
+ }
126
+
127
+ if (templateObject.templateFolder().includes(`${sep}modules${sep}`)) {
128
+ throw Error(`Cannot add cards to imported module templates`);
129
+ }
130
+
131
+ // Collect all add-card promises and settle them in parallel.
132
+ const promiseContainer = [];
133
+ const cardsContainer: string[] = [];
134
+ for (let cardCount = 0; cardCount < count; ++cardCount) {
135
+ promiseContainer.push(templateObject.addCard(cardTypeName, specificCard));
136
+ }
137
+ const promisesResult = await Promise.allSettled(promiseContainer).then(
138
+ (results) => {
139
+ for (const result of results) {
140
+ if (result.status !== 'fulfilled') {
141
+ throw new Error(result.reason);
142
+ }
143
+ cardsContainer.push(result.value);
144
+ }
145
+ },
146
+ );
147
+
148
+ if (cardsContainer.length === 0) {
149
+ throw new Error(`Invalid value for 'repeat:' "${count}"`);
150
+ }
151
+
152
+ if (promisesResult === undefined) {
153
+ return cardsContainer;
154
+ } else {
155
+ throw new Error('Unknown error');
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Adds an attachment to a card.
161
+ * @param cardKey card ID
162
+ * @param attachment path to an attachment file or attachment name if buffer is defined
163
+ * @param buffer (Optional) attachment buffer
164
+ */
165
+ public async createAttachment(
166
+ cardKey: string,
167
+ attachment: string,
168
+ buffer?: Buffer,
169
+ ) {
170
+ if (!buffer && !pathExists(attachment)) {
171
+ throw new Error(
172
+ `Input validation error: cannot find attachment '${attachment}'`,
173
+ );
174
+ }
175
+ const attachmentFolder = await this.project.cardAttachmentFolder(cardKey);
176
+ if (!attachmentFolder) {
177
+ throw new Error(`Attachment folder for '${cardKey}' not found`);
178
+ }
179
+
180
+ // Imported templates cannot be modified.
181
+ if (attachmentFolder.includes(`${sep}modules${sep}`)) {
182
+ throw new Error(`Cannot modify imported module`);
183
+ }
184
+
185
+ try {
186
+ await mkdir(attachmentFolder, { recursive: true }).then(async () => {
187
+ if (!buffer) {
188
+ return copyFile(
189
+ attachment,
190
+ join(attachmentFolder, basename(attachment)),
191
+ fsConstants.COPYFILE_EXCL,
192
+ );
193
+ }
194
+ return writeFile(join(attachmentFolder, basename(attachment)), buffer, {
195
+ flag: 'wx',
196
+ });
197
+ });
198
+ } catch (error) {
199
+ throw new Error(errorFunction(error));
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Creates card(s) to a project. All cards from template are instantiated to the project.
205
+ * @param templateName name of a template to use
206
+ * @param parentCardKey (Optional) card-key of a parent card. If missing, cards are added to the card root.
207
+ * @returns array of card keys that were created. Cards are sorted by their parent key and rank. Template root cards are first but the order between other card groups is not guaranteed. However, the order of cards within a group is guaranteed to be ordered by rank.
208
+ */
209
+ public async createCard(
210
+ templateName: string,
211
+ parentCardKey?: string,
212
+ ): Promise<Card[]> {
213
+ const templateResource = new TemplateResource(
214
+ this.project,
215
+ resourceName(templateName),
216
+ );
217
+
218
+ await Validate.getInstance().validResourceName(
219
+ 'templates',
220
+ resourceNameToString(resourceName(templateName)),
221
+ await this.project.projectPrefixes(),
222
+ );
223
+
224
+ await templateResource.validate();
225
+
226
+ const specificCard = parentCardKey
227
+ ? await this.project.findSpecificCard(parentCardKey, {
228
+ metadata: true,
229
+ children: true,
230
+ })
231
+ : undefined;
232
+ if (parentCardKey && !specificCard) {
233
+ throw new Error(`Card '${parentCardKey}' not found from project`);
234
+ }
235
+
236
+ const templateObject = templateResource.templateObject();
237
+ if (!templateObject || !templateObject.isCreated()) {
238
+ throw new Error(`Template '${templateName}' not found from project`);
239
+ }
240
+
241
+ const createdCards = await templateObject.createCards(specificCard);
242
+ if (createdCards.length > 0) {
243
+ await this.calculateCmd.handleNewCards(createdCards);
244
+ // Note: This assumes that parent keys will be ahead of 'a' in the sort order.
245
+ const sorted = sortItems(createdCards, (item) => {
246
+ return `${item.parent === 'root' ? 'a' : item.parent}${item.metadata?.rank || EMPTY_RANK}`;
247
+ });
248
+ return sorted;
249
+ }
250
+ return [];
251
+ }
252
+
253
+ /**
254
+ * Creates a card type.
255
+ * @param cardTypeName name for the card type.
256
+ * @param workflowName workflow name to use in the card type.
257
+ */
258
+ public async createCardType(cardTypeName: string, workflowName: string) {
259
+ const cardType = new CardTypeResource(
260
+ this.project,
261
+ resourceName(cardTypeName),
262
+ );
263
+
264
+ await cardType.createCardType(workflowName);
265
+ }
266
+
267
+ /**
268
+ * Creates a new field type.
269
+ * @param fieldTypeName name for the field type.
270
+ * @param dataType data type for the field type
271
+ */
272
+ public async createFieldType(fieldTypeName: string, dataType: DataType) {
273
+ const fieldType = new FieldTypeResource(
274
+ this.project,
275
+ resourceName(fieldTypeName),
276
+ );
277
+ await fieldType.createFieldType(dataType);
278
+ }
279
+
280
+ /**
281
+ * Creates a new graph model.
282
+ * @param graphModelName name for the graph model.
283
+ */
284
+ public async createGraphModel(graphModelName: string) {
285
+ const graphModel = new GraphModelResource(
286
+ this.project,
287
+ resourceName(graphModelName),
288
+ );
289
+ await graphModel.create();
290
+ }
291
+
292
+ /**
293
+ * Creates a new graph view.
294
+ * @param graphModelName name for the graph view.
295
+ */
296
+ public async createGraphView(graphViewName: string) {
297
+ const graphView = new GraphViewResource(
298
+ this.project,
299
+ resourceName(graphViewName),
300
+ );
301
+ await graphView.create();
302
+ }
303
+
304
+ /**
305
+ * Creates a label in the given card
306
+ * @param cardKey The card to which the label is added to
307
+ * @param label The label being added
308
+ */
309
+ public async createLabel(cardKey: string, label: string) {
310
+ if (!Validate.isValidLabelName(label)) {
311
+ throw new Error(`Not a valid label name'`);
312
+ }
313
+
314
+ const card = await this.project.findSpecificCard(cardKey, {
315
+ metadata: true,
316
+ });
317
+ if (!card) {
318
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
319
+ }
320
+ const labels = card.metadata?.labels ?? [];
321
+
322
+ if (labels.includes(label)) {
323
+ throw new Error('Label already exists');
324
+ }
325
+
326
+ labels.push(label);
327
+ return this.project.updateCardMetadataKey(cardKey, 'labels', labels);
328
+ }
329
+
330
+ /**
331
+ * Creates a new link type.
332
+ * @param linkTypeName name for the link type.
333
+ */
334
+ public async createLinkType(linkTypeName: string) {
335
+ const linkType = new LinkTypeResource(
336
+ this.project,
337
+ resourceName(linkTypeName),
338
+ );
339
+ await linkType.create();
340
+ }
341
+
342
+ /**
343
+ * Creates a link between two cards.
344
+ * @param cardKey The card to update
345
+ * @param destinationCardKey The card to link to
346
+ * @param linkType The type of link to add
347
+ * @param linkDescription Optional description of the link
348
+ */
349
+ public async createLink(
350
+ cardKey: string,
351
+ destinationCardKey: string,
352
+ linkType: string,
353
+ linkDescription?: string,
354
+ ) {
355
+ if (cardKey === destinationCardKey) {
356
+ throw new Error('Cannot link card to itself');
357
+ }
358
+
359
+ // Determine the card path
360
+ const card = await this.project.findSpecificCard(cardKey, {
361
+ metadata: true,
362
+ });
363
+ if (!card) {
364
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
365
+ }
366
+
367
+ const destinationCard = await this.project.findSpecificCard(
368
+ destinationCardKey,
369
+ {
370
+ metadata: true,
371
+ },
372
+ );
373
+ if (!destinationCard) {
374
+ throw new Error(
375
+ `Card '${destinationCardKey}' does not exist in the project`,
376
+ );
377
+ }
378
+ // make sure the link type exists
379
+ const linkTypeObject = await this.project.resource<LinkType>(linkType);
380
+ if (!linkTypeObject) {
381
+ throw new Error(`Link type '${linkType}' does not exist in the project`);
382
+ }
383
+
384
+ // make sure that if linkDescription is not enabled, linkDescription is not provided
385
+ if (
386
+ !linkTypeObject.enableLinkDescription &&
387
+ linkDescription !== undefined
388
+ ) {
389
+ throw new Error(
390
+ `Link type '${linkType}' does not allow link description`,
391
+ );
392
+ }
393
+
394
+ // make sure source card key exists in the link type sourceCardTypes
395
+ // if sourceCardTypes is empty, any card can be linked
396
+ if (
397
+ linkTypeObject.sourceCardTypes.length > 0 &&
398
+ !linkTypeObject.sourceCardTypes.includes(card.metadata!.cardType)
399
+ ) {
400
+ throw new Error(
401
+ `Card type '${card.metadata?.cardType}' cannot be linked with link type '${linkType}'`,
402
+ );
403
+ }
404
+
405
+ // make sure destination card key exists in the link type destinationCardTypes
406
+ // if destinationCardTypes is empty, any card can be linked
407
+ if (
408
+ linkTypeObject.destinationCardTypes.length > 0 &&
409
+ !linkTypeObject.destinationCardTypes.includes(
410
+ destinationCard.metadata!.cardType,
411
+ )
412
+ ) {
413
+ throw new Error(
414
+ `Card type '${destinationCard.metadata!.cardType}' cannot be linked with link type '${linkType}'`,
415
+ );
416
+ }
417
+
418
+ // if contains the same link, do not add it again
419
+ const existingLink = card.metadata?.links.find(
420
+ (l) =>
421
+ l.linkType === linkType &&
422
+ l.cardKey === destinationCardKey &&
423
+ l.linkDescription === linkDescription,
424
+ );
425
+ if (existingLink) {
426
+ throw new Error(
427
+ `Link from card '${cardKey}' to card '${destinationCardKey}' already exists`,
428
+ );
429
+ }
430
+
431
+ const links: Link[] = card.metadata?.links || [];
432
+ links.push({
433
+ linkType,
434
+ cardKey: destinationCardKey,
435
+ linkDescription,
436
+ });
437
+
438
+ await this.project.updateCardMetadataKey(cardKey, 'links', links);
439
+ }
440
+
441
+ /**
442
+ * Creates a new project.
443
+ * @param projectPath where to create the project.
444
+ * @param projectPrefix prefix for the project.
445
+ * @param projectName name for the project.
446
+ */
447
+ public static async createProject(
448
+ projectPath: string,
449
+ projectPrefix: string,
450
+ projectName: string,
451
+ ) {
452
+ projectPath = resolve(projectPath);
453
+
454
+ if (!projectPath) {
455
+ throw new Error('Cannot create project without a path');
456
+ }
457
+
458
+ const projectFolders: string[] = ['.cards/local', 'cardRoot'];
459
+
460
+ if (
461
+ projectPrefix === undefined ||
462
+ projectPrefix.length < 3 ||
463
+ projectPrefix.length > 10
464
+ ) {
465
+ throw new Error(
466
+ `Input validation error: prefix must be from 3 to 10 characters long. '${projectPrefix}' does not fulfill the condition.`,
467
+ );
468
+ }
469
+ if (!Validate.isValidProjectName(projectName)) {
470
+ throw new Error(
471
+ `Input validation error: invalid project name '${projectName}'`,
472
+ );
473
+ }
474
+ if (!Validate.validatePrefix(projectPrefix)) {
475
+ throw new Error(
476
+ `Input validation error: invalid prefix '${projectPrefix}'`,
477
+ );
478
+ }
479
+
480
+ if (Project.isCreated(projectPath)) {
481
+ throw new Error('Project already exists');
482
+ }
483
+
484
+ if (!Validate.isValidProjectName(projectName)) {
485
+ throw new Error(
486
+ `Input validation error: invalid project name '${projectName}'`,
487
+ );
488
+ }
489
+
490
+ await mkdir(projectPath, { recursive: true }).then(async () => {
491
+ return await Promise.all(
492
+ projectFolders.map((folder) =>
493
+ mkdir(`${projectPath}/${folder}`, { recursive: true }),
494
+ ),
495
+ );
496
+ });
497
+ Create.JSONFileContent.forEach(async (entry) => {
498
+ if ('cardKeyPrefix' in entry.content) {
499
+ if (entry.content.cardKeyPrefix.includes('$PROJECT-PREFIX')) {
500
+ entry.content.cardKeyPrefix = projectPrefix.toLowerCase();
501
+ }
502
+ if (entry.content.name.includes('$PROJECT-NAME')) {
503
+ entry.content.name = projectName;
504
+ }
505
+ }
506
+ await writeJsonFile(
507
+ join(projectPath, entry.path, entry.name),
508
+ entry.content,
509
+ );
510
+ });
511
+
512
+ try {
513
+ await writeFile(join(projectPath, '.gitignore'), this.gitIgnoreContent);
514
+ } catch {
515
+ console.error('Failed to create project');
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Creates a report
521
+ * @param name name of the report
522
+ */
523
+ public async createReport(name: string) {
524
+ const report = new ReportResource(this.project, resourceName(name));
525
+ await report.createReport();
526
+ }
527
+
528
+ /**
529
+ * Creates a new template to a project.
530
+ * @param templateName Name of the template.
531
+ * @param templateContent JSON content for the template file.
532
+ */
533
+ public async createTemplate(templateName: string, templateContent: string) {
534
+ const template = new TemplateResource(
535
+ this.project,
536
+ resourceName(templateName),
537
+ );
538
+
539
+ await template.create(
540
+ templateContent ? JSON.parse(templateContent) : undefined,
541
+ );
542
+ }
543
+
544
+ /**
545
+ * Creates a workflow.
546
+ * @param workflowName workflow name
547
+ * @param workflowContent workflow content JSON
548
+ */
549
+ public async createWorkflow(workflowName: string, workflowContent: string) {
550
+ const workflow = new WorkflowResource(
551
+ this.project,
552
+ resourceName(workflowName),
553
+ );
554
+
555
+ await workflow.create(
556
+ workflowContent ? JSON.parse(workflowContent) : undefined,
557
+ );
558
+ }
559
+ }
@@ -0,0 +1,123 @@
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
+ // node
14
+ import { join } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+ import { spawnSync } from 'node:child_process';
17
+
18
+ import { ActionGuard } from '../permissions/action-guard.js';
19
+ import type { Calculate } from './index.js';
20
+ import type { MetadataContent } from '../interfaces/project-interfaces.js';
21
+ import { Project } from '../containers/project.js';
22
+ import { UserPreferences } from '../utils/user-preferences.js';
23
+
24
+ export class Edit {
25
+ private project: Project;
26
+ private calculateCmd: Calculate;
27
+
28
+ constructor(project: Project, calculateCmd: Calculate) {
29
+ this.project = project;
30
+ this.calculateCmd = calculateCmd;
31
+ }
32
+
33
+ /**
34
+ * Opens the content and metadata files for a card in the code editor
35
+ * @param cardKey - The key of the card to open. Required.
36
+ */
37
+ public async editCard(cardKey: string) {
38
+ // Determine the card path
39
+ const cardPath = this.project.pathToCard(cardKey);
40
+ if (!cardPath) {
41
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
42
+ }
43
+
44
+ // Read the user preferences
45
+ const prefs = new UserPreferences(
46
+ join(homedir(), '.cyberismo', 'cards.prefs.json'),
47
+ ).getPreferences();
48
+
49
+ // Construct paths for the card components (json and adoc)
50
+ const cardDirPath = join(this.project.paths.cardRootFolder, cardPath);
51
+ const cardContentPath = join(cardDirPath, Project.cardContentFile);
52
+ const cardJsonPath = join(cardDirPath, Project.cardMetadataFile);
53
+
54
+ // Extract the editor settings from the preferences.
55
+ const editorPrefs = prefs.editCommand[process.platform];
56
+ const editorCommand = editorPrefs.command;
57
+ const editorArgPrefs: string[] = editorPrefs.args; // Coarces the args to string[] for map() below
58
+
59
+ // Poor man's handlebars/moustache replacements
60
+ const editorArgs = editorArgPrefs.map((arg) => {
61
+ arg = arg.replace(/\{\{\s*cardContentPath\s*\}\}/, cardContentPath);
62
+ arg = arg.replace(/\{\{\s*cardJsonPath\s*\}\}/, cardJsonPath);
63
+ arg = arg.replace(/\{\{\s*cardDirPath\s*\}\}/, cardDirPath);
64
+
65
+ return arg;
66
+ });
67
+
68
+ // Execute the editor synchronously and set it to inherit the parent process stdio.
69
+ // This enables terminal editors such as vim to grab the screen I/O
70
+ // from the command line cards process.
71
+ const result = spawnSync(editorCommand, editorArgs, {
72
+ stdio: 'inherit',
73
+ });
74
+
75
+ if (result.status === null) {
76
+ throw new Error(`Cannot launch editor: ${result.error}`);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Updates card content (index.adoc) with incoming content.
82
+ * @param cardKey The card to update.
83
+ * @param changedContent New content for the card.
84
+ */
85
+ public async editCardContent(cardKey: string, changedContent: string) {
86
+ // Determine the card path
87
+ const cardPath = this.project.pathToCard(cardKey);
88
+ if (!cardPath) {
89
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
90
+ }
91
+
92
+ const actionGuard = new ActionGuard(this.calculateCmd);
93
+ await actionGuard.checkPermission('editContent', cardKey);
94
+
95
+ await this.project.updateCardContent(cardKey, changedContent);
96
+ }
97
+
98
+ /**
99
+ * Updates card metadata.
100
+ * @param cardKey The card to update
101
+ * @param changedKey Which metadata property was changed
102
+ * @param newValue New value for the metadata property
103
+ */
104
+ public async editCardMetadata(
105
+ cardKey: string,
106
+ changedKey: string,
107
+ newValue: MetadataContent,
108
+ ) {
109
+ // Determine the card path
110
+ const cardPath = this.project.pathToCard(cardKey);
111
+ if (!cardPath) {
112
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
113
+ }
114
+ if (!changedKey) {
115
+ throw new Error(`Changed key cannot be empty`);
116
+ }
117
+
118
+ // check for editing rights
119
+ const actionGuard = new ActionGuard(this.calculateCmd);
120
+ await actionGuard.checkPermission('editField', cardKey, changedKey);
121
+ await this.project.updateCardMetadataKey(cardKey, changedKey, newValue);
122
+ }
123
+ }