@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.
- package/LICENSE +702 -0
- package/dist/card-metadata-updater.d.ts +33 -0
- package/dist/card-metadata-updater.js +121 -0
- package/dist/card-metadata-updater.js.map +1 -0
- package/dist/command-handler.d.ts +96 -0
- package/dist/command-handler.js +557 -0
- package/dist/command-handler.js.map +1 -0
- package/dist/command-manager.d.ts +43 -0
- package/dist/command-manager.js +73 -0
- package/dist/command-manager.js.map +1 -0
- package/dist/commands/calculate.d.ts +86 -0
- package/dist/commands/calculate.js +444 -0
- package/dist/commands/calculate.js.map +1 -0
- package/dist/commands/create.d.ts +114 -0
- package/dist/commands/create.js +389 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/edit.d.ts +37 -0
- package/dist/commands/edit.js +99 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/export-site.d.ts +45 -0
- package/dist/commands/export-site.js +301 -0
- package/dist/commands/export-site.js.map +1 -0
- package/dist/commands/export.d.ts +53 -0
- package/dist/commands/export.js +251 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/import.d.ts +53 -0
- package/dist/commands/import.js +133 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/index.d.ts +26 -0
- package/dist/commands/index.js +27 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/move.d.ts +55 -0
- package/dist/commands/move.js +341 -0
- package/dist/commands/move.js.map +1 -0
- package/dist/commands/remove.d.ts +38 -0
- package/dist/commands/remove.js +192 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/rename.d.ts +46 -0
- package/dist/commands/rename.js +289 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/show.d.ts +124 -0
- package/dist/commands/show.js +345 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/transition.d.ts +27 -0
- package/dist/commands/transition.js +92 -0
- package/dist/commands/transition.js.map +1 -0
- package/dist/commands/update.d.ts +29 -0
- package/dist/commands/update.js +64 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +143 -0
- package/dist/commands/validate.js +689 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/containers/card-container.d.ts +44 -0
- package/dist/containers/card-container.js +282 -0
- package/dist/containers/card-container.js.map +1 -0
- package/dist/containers/project/project-paths.d.ts +46 -0
- package/dist/containers/project/project-paths.js +105 -0
- package/dist/containers/project/project-paths.js.map +1 -0
- package/dist/containers/project/resource-collector.d.ts +86 -0
- package/dist/containers/project/resource-collector.js +331 -0
- package/dist/containers/project/resource-collector.js.map +1 -0
- package/dist/containers/project.d.ts +351 -0
- package/dist/containers/project.js +896 -0
- package/dist/containers/project.js.map +1 -0
- package/dist/containers/template.d.ts +108 -0
- package/dist/containers/template.js +433 -0
- package/dist/containers/template.js.map +1 -0
- package/dist/exceptions/index.d.ts +19 -0
- package/dist/exceptions/index.js +26 -0
- package/dist/exceptions/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/adoc.d.ts +12 -0
- package/dist/interfaces/adoc.js +13 -0
- package/dist/interfaces/adoc.js.map +1 -0
- package/dist/interfaces/macros.d.ts +45 -0
- package/dist/interfaces/macros.js +13 -0
- package/dist/interfaces/macros.js.map +1 -0
- package/dist/interfaces/project-interfaces.d.ts +121 -0
- package/dist/interfaces/project-interfaces.js +21 -0
- package/dist/interfaces/project-interfaces.js.map +1 -0
- package/dist/interfaces/request-status-interfaces.d.ts +28 -0
- package/dist/interfaces/request-status-interfaces.js +20 -0
- package/dist/interfaces/request-status-interfaces.js.map +1 -0
- package/dist/interfaces/resource-interfaces.d.ts +117 -0
- package/dist/interfaces/resource-interfaces.js +20 -0
- package/dist/interfaces/resource-interfaces.js.map +1 -0
- package/dist/macros/base-macro.d.ts +31 -0
- package/dist/macros/base-macro.js +126 -0
- package/dist/macros/base-macro.js.map +1 -0
- package/dist/macros/common.d.ts +17 -0
- package/dist/macros/common.js +23 -0
- package/dist/macros/common.js.map +1 -0
- package/dist/macros/createCards/index.d.ts +36 -0
- package/dist/macros/createCards/index.js +35 -0
- package/dist/macros/createCards/index.js.map +1 -0
- package/dist/macros/createCards/metadata.d.ts +14 -0
- package/dist/macros/createCards/metadata.js +18 -0
- package/dist/macros/createCards/metadata.js.map +1 -0
- package/dist/macros/graph/index.d.ts +29 -0
- package/dist/macros/graph/index.js +91 -0
- package/dist/macros/graph/index.js.map +1 -0
- package/dist/macros/graph/metadata.d.ts +14 -0
- package/dist/macros/graph/metadata.js +18 -0
- package/dist/macros/graph/metadata.js.map +1 -0
- package/dist/macros/index.d.ts +93 -0
- package/dist/macros/index.js +237 -0
- package/dist/macros/index.js.map +1 -0
- package/dist/macros/report/index.d.ts +26 -0
- package/dist/macros/report/index.js +70 -0
- package/dist/macros/report/index.js.map +1 -0
- package/dist/macros/report/metadata.d.ts +14 -0
- package/dist/macros/report/metadata.js +18 -0
- package/dist/macros/report/metadata.js.map +1 -0
- package/dist/macros/scoreCard/index.d.ts +30 -0
- package/dist/macros/scoreCard/index.js +38 -0
- package/dist/macros/scoreCard/index.js.map +1 -0
- package/dist/macros/scoreCard/metadata.d.ts +14 -0
- package/dist/macros/scoreCard/metadata.js +18 -0
- package/dist/macros/scoreCard/metadata.js.map +1 -0
- package/dist/macros/task-queue.d.ts +46 -0
- package/dist/macros/task-queue.js +69 -0
- package/dist/macros/task-queue.js.map +1 -0
- package/dist/module-manager.d.ts +62 -0
- package/dist/module-manager.js +350 -0
- package/dist/module-manager.js.map +1 -0
- package/dist/permissions/action-guard.d.ts +28 -0
- package/dist/permissions/action-guard.js +61 -0
- package/dist/permissions/action-guard.js.map +1 -0
- package/dist/project-settings.d.ts +42 -0
- package/dist/project-settings.js +120 -0
- package/dist/project-settings.js.map +1 -0
- package/dist/resources/array-handler.d.ts +28 -0
- package/dist/resources/array-handler.js +116 -0
- package/dist/resources/array-handler.js.map +1 -0
- package/dist/resources/card-type-resource.d.ts +72 -0
- package/dist/resources/card-type-resource.js +334 -0
- package/dist/resources/card-type-resource.js.map +1 -0
- package/dist/resources/create-defaults.d.ts +81 -0
- package/dist/resources/create-defaults.js +184 -0
- package/dist/resources/create-defaults.js.map +1 -0
- package/dist/resources/field-type-resource.d.ts +88 -0
- package/dist/resources/field-type-resource.js +411 -0
- package/dist/resources/field-type-resource.js.map +1 -0
- package/dist/resources/file-resource.d.ts +50 -0
- package/dist/resources/file-resource.js +301 -0
- package/dist/resources/file-resource.js.map +1 -0
- package/dist/resources/folder-resource.d.ts +66 -0
- package/dist/resources/folder-resource.js +100 -0
- package/dist/resources/folder-resource.js.map +1 -0
- package/dist/resources/graph-model-resource.d.ts +78 -0
- package/dist/resources/graph-model-resource.js +164 -0
- package/dist/resources/graph-model-resource.js.map +1 -0
- package/dist/resources/graph-view-resource.d.ts +78 -0
- package/dist/resources/graph-view-resource.js +163 -0
- package/dist/resources/graph-view-resource.js.map +1 -0
- package/dist/resources/link-type-resource.d.ts +62 -0
- package/dist/resources/link-type-resource.js +150 -0
- package/dist/resources/link-type-resource.js.map +1 -0
- package/dist/resources/report-resource.d.ts +77 -0
- package/dist/resources/report-resource.js +171 -0
- package/dist/resources/report-resource.js.map +1 -0
- package/dist/resources/resource-object.d.ts +108 -0
- package/dist/resources/resource-object.js +147 -0
- package/dist/resources/resource-object.js.map +1 -0
- package/dist/resources/template-resource.d.ts +82 -0
- package/dist/resources/template-resource.js +173 -0
- package/dist/resources/template-resource.js.map +1 -0
- package/dist/resources/workflow-resource.d.ts +67 -0
- package/dist/resources/workflow-resource.js +156 -0
- package/dist/resources/workflow-resource.js.map +1 -0
- package/dist/types/queries.d.ts +142 -0
- package/dist/types/queries.js +16 -0
- package/dist/types/queries.js.map +1 -0
- package/dist/utils/card-utils.d.ts +34 -0
- package/dist/utils/card-utils.js +78 -0
- package/dist/utils/card-utils.js.map +1 -0
- package/dist/utils/clingo-fact-builder.d.ts +58 -0
- package/dist/utils/clingo-fact-builder.js +126 -0
- package/dist/utils/clingo-fact-builder.js.map +1 -0
- package/dist/utils/clingo-facts.d.ts +97 -0
- package/dist/utils/clingo-facts.js +352 -0
- package/dist/utils/clingo-facts.js.map +1 -0
- package/dist/utils/clingo-parser.d.ts +59 -0
- package/dist/utils/clingo-parser.js +403 -0
- package/dist/utils/clingo-parser.js.map +1 -0
- package/dist/utils/clingo-program-builder.d.ts +39 -0
- package/dist/utils/clingo-program-builder.js +57 -0
- package/dist/utils/clingo-program-builder.js.map +1 -0
- package/dist/utils/common-utils.d.ts +24 -0
- package/dist/utils/common-utils.js +47 -0
- package/dist/utils/common-utils.js.map +1 -0
- package/dist/utils/constants.d.ts +18 -0
- package/dist/utils/constants.js +27 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/csv.d.ts +18 -0
- package/dist/utils/csv.js +45 -0
- package/dist/utils/csv.js.map +1 -0
- package/dist/utils/file-utils.d.ts +69 -0
- package/dist/utils/file-utils.js +158 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/json.d.ts +61 -0
- package/dist/utils/json.js +108 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/lexorank.d.ts +59 -0
- package/dist/utils/lexorank.js +159 -0
- package/dist/utils/lexorank.js.map +1 -0
- package/dist/utils/log-utils.d.ts +40 -0
- package/dist/utils/log-utils.js +109 -0
- package/dist/utils/log-utils.js.map +1 -0
- package/dist/utils/random.d.ts +19 -0
- package/dist/utils/random.js +34 -0
- package/dist/utils/random.js.map +1 -0
- package/dist/utils/resource-utils.d.ts +45 -0
- package/dist/utils/resource-utils.js +137 -0
- package/dist/utils/resource-utils.js.map +1 -0
- package/dist/utils/sanitize-svg.d.ts +18 -0
- package/dist/utils/sanitize-svg.js +38 -0
- package/dist/utils/sanitize-svg.js.map +1 -0
- package/dist/utils/user-preferences.d.ts +64 -0
- package/dist/utils/user-preferences.js +106 -0
- package/dist/utils/user-preferences.js.map +1 -0
- package/dist/utils/validate.d.ts +26 -0
- package/dist/utils/validate.js +53 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/utils/value-utils.d.ts +58 -0
- package/dist/utils/value-utils.js +181 -0
- package/dist/utils/value-utils.js.map +1 -0
- package/package.json +67 -0
- package/src/card-metadata-updater.ts +182 -0
- package/src/command-handler.ts +686 -0
- package/src/command-manager.ts +99 -0
- package/src/commands/calculate.ts +591 -0
- package/src/commands/create.ts +559 -0
- package/src/commands/edit.ts +123 -0
- package/src/commands/export-site.ts +356 -0
- package/src/commands/export.ts +315 -0
- package/src/commands/import.ts +169 -0
- package/src/commands/index.ts +42 -0
- package/src/commands/move.ts +451 -0
- package/src/commands/remove.ts +244 -0
- package/src/commands/rename.ts +378 -0
- package/src/commands/show.ts +442 -0
- package/src/commands/transition.ts +127 -0
- package/src/commands/update.ts +76 -0
- package/src/commands/validate.ts +962 -0
- package/src/containers/card-container.ts +378 -0
- package/src/containers/project/project-paths.ts +127 -0
- package/src/containers/project/resource-collector.ts +379 -0
- package/src/containers/project.ts +1135 -0
- package/src/containers/template.ts +573 -0
- package/src/exceptions/index.ts +29 -0
- package/src/index.ts +33 -0
- package/src/interfaces/adoc.ts +18 -0
- package/src/interfaces/macros.ts +54 -0
- package/src/interfaces/project-interfaces.ts +208 -0
- package/src/interfaces/request-status-interfaces.ts +30 -0
- package/src/interfaces/resource-interfaces.ts +179 -0
- package/src/macros/base-macro.ts +176 -0
- package/src/macros/common.ts +24 -0
- package/src/macros/createCards/index.ts +57 -0
- package/src/macros/createCards/metadata.ts +21 -0
- package/src/macros/graph/index.ts +130 -0
- package/src/macros/graph/metadata.ts +21 -0
- package/src/macros/index.ts +321 -0
- package/src/macros/report/index.ts +88 -0
- package/src/macros/report/metadata.ts +21 -0
- package/src/macros/scoreCard/index.ts +55 -0
- package/src/macros/scoreCard/metadata.ts +21 -0
- package/src/macros/task-queue.ts +79 -0
- package/src/module-manager.ts +443 -0
- package/src/permissions/action-guard.ts +77 -0
- package/src/project-settings.ts +140 -0
- package/src/resources/array-handler.ts +141 -0
- package/src/resources/card-type-resource.ts +455 -0
- package/src/resources/create-defaults.ts +216 -0
- package/src/resources/field-type-resource.ts +533 -0
- package/src/resources/file-resource.ts +433 -0
- package/src/resources/folder-resource.ts +140 -0
- package/src/resources/graph-model-resource.ts +205 -0
- package/src/resources/graph-view-resource.ts +199 -0
- package/src/resources/link-type-resource.ts +191 -0
- package/src/resources/report-resource.ts +224 -0
- package/src/resources/resource-object.ts +246 -0
- package/src/resources/template-resource.ts +210 -0
- package/src/resources/workflow-resource.ts +205 -0
- package/src/types/queries.ts +149 -0
- package/src/utils/card-utils.ts +83 -0
- package/src/utils/clingo-fact-builder.ts +167 -0
- package/src/utils/clingo-facts.ts +550 -0
- package/src/utils/clingo-parser.ts +519 -0
- package/src/utils/clingo-program-builder.ts +71 -0
- package/src/utils/common-utils.ts +54 -0
- package/src/utils/constants.ts +32 -0
- package/src/utils/csv.ts +53 -0
- package/src/utils/file-utils.ts +182 -0
- package/src/utils/json.ts +118 -0
- package/src/utils/lexorank.ts +180 -0
- package/src/utils/log-utils.ts +127 -0
- package/src/utils/random.ts +37 -0
- package/src/utils/resource-utils.ts +180 -0
- package/src/utils/sanitize-svg.ts +46 -0
- package/src/utils/user-preferences.ts +126 -0
- package/src/utils/validate.ts +66 -0
- package/src/utils/value-utils.ts +189 -0
|
@@ -0,0 +1,533 @@
|
|
|
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 type {
|
|
15
|
+
Card,
|
|
16
|
+
ChangeOperation,
|
|
17
|
+
Operation,
|
|
18
|
+
Project,
|
|
19
|
+
RemoveOperation,
|
|
20
|
+
ResourceName,
|
|
21
|
+
} from './file-resource.js';
|
|
22
|
+
import {
|
|
23
|
+
DefaultContent,
|
|
24
|
+
FileResource,
|
|
25
|
+
ResourcesFrom,
|
|
26
|
+
resourceName,
|
|
27
|
+
resourceNameToString,
|
|
28
|
+
sortCards,
|
|
29
|
+
} from './file-resource.js';
|
|
30
|
+
|
|
31
|
+
import type {
|
|
32
|
+
CardType,
|
|
33
|
+
DataType,
|
|
34
|
+
EnumDefinition,
|
|
35
|
+
FieldType,
|
|
36
|
+
} from '../interfaces/resource-interfaces.js';
|
|
37
|
+
import { CardTypeResource } from './card-type-resource.js';
|
|
38
|
+
import {
|
|
39
|
+
allowed,
|
|
40
|
+
fromDate,
|
|
41
|
+
fromNumber,
|
|
42
|
+
fromString,
|
|
43
|
+
} from '../utils/value-utils.js';
|
|
44
|
+
import * as EmailValidator from 'email-validator';
|
|
45
|
+
|
|
46
|
+
const SHORT_TEXT_MAX_LENGTH = 80;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Field type resource class.
|
|
50
|
+
*/
|
|
51
|
+
export class FieldTypeResource extends FileResource {
|
|
52
|
+
// Initialize data type change helpers (fromType, toType) to some values.
|
|
53
|
+
// The actual types are set, if this Field Type's dataType is changed.
|
|
54
|
+
private fromType: DataType = 'integer';
|
|
55
|
+
private toType: DataType = 'integer';
|
|
56
|
+
|
|
57
|
+
constructor(project: Project, name: ResourceName) {
|
|
58
|
+
super(project, name, 'fieldTypes');
|
|
59
|
+
|
|
60
|
+
this.contentSchemaId = 'fieldTypeSchema';
|
|
61
|
+
this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
|
|
62
|
+
|
|
63
|
+
this.initialize();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Cards from given array that include this field type.
|
|
67
|
+
private cardsWithFieldType(cards: Card[]): string[] {
|
|
68
|
+
const resourceName = resourceNameToString(this.resourceName);
|
|
69
|
+
return cards
|
|
70
|
+
.filter((card) => card.metadata?.[resourceName])
|
|
71
|
+
.map((card) => card.key);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Collects affected cards.
|
|
75
|
+
private async collectCards(cardContent: object, cardTypeName: string) {
|
|
76
|
+
async function filteredCards(
|
|
77
|
+
cardSource: Promise<Card[]>,
|
|
78
|
+
cardTypeName: string,
|
|
79
|
+
): Promise<Card[]> {
|
|
80
|
+
const cards = await cardSource;
|
|
81
|
+
return cards.filter((card) => card.metadata?.cardType === cardTypeName);
|
|
82
|
+
}
|
|
83
|
+
return filteredCards(
|
|
84
|
+
this.project.cards(this.project.paths.cardRootFolder, cardContent),
|
|
85
|
+
cardTypeName,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Converts values.
|
|
90
|
+
// The allowed conversions are:
|
|
91
|
+
// - shortText/longText --> person, if valid email
|
|
92
|
+
// - shortText/longText --> integer/number, if can be parseNumber/parseInt'd
|
|
93
|
+
// - shortText/longText --> list, if text can be split with comma
|
|
94
|
+
// - shortText/longText --> date / datetime (if it can be parsed as date)
|
|
95
|
+
// - shortText/longText --> boolean, if string is "false" or "true"
|
|
96
|
+
// - number --> integer, drop fractions
|
|
97
|
+
// - integer --> number
|
|
98
|
+
// - date --> dateTime
|
|
99
|
+
// - dateTime --> date
|
|
100
|
+
// - any --> shortText, unless too long
|
|
101
|
+
// - any --> longText
|
|
102
|
+
// Other cases are forbidden.
|
|
103
|
+
private doConvertValue<T>(value: T) {
|
|
104
|
+
if (this.fromType === 'date' || this.fromType === 'dateTime') {
|
|
105
|
+
return fromDate(value, this.toType);
|
|
106
|
+
}
|
|
107
|
+
if (this.fromType === 'integer' || this.fromType === 'number') {
|
|
108
|
+
return fromNumber(value, this.toType);
|
|
109
|
+
}
|
|
110
|
+
if (this.fromType === 'shortText' || this.fromType === 'longText') {
|
|
111
|
+
return fromString(value, this.toType);
|
|
112
|
+
}
|
|
113
|
+
if (this.toType === 'shortText' || this.toType === 'longText') {
|
|
114
|
+
let tempValue = String(value);
|
|
115
|
+
tempValue = tempValue.replace(/(\\")/g, '');
|
|
116
|
+
if (
|
|
117
|
+
this.toType === 'shortText' &&
|
|
118
|
+
tempValue.length > SHORT_TEXT_MAX_LENGTH
|
|
119
|
+
) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return tempValue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Converts value 'fromType' to 'toType'. If value cannot be converted returns null.
|
|
127
|
+
private convertValue<T>(value: T) {
|
|
128
|
+
if (value === null) return null;
|
|
129
|
+
if (value === undefined) return undefined;
|
|
130
|
+
|
|
131
|
+
const tempValue = this.doConvertValue(value);
|
|
132
|
+
if (tempValue === null) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Cannot convert from '${this.fromType}' to '${this.toType}' value '${value}'`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return tempValue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// If dataType has changed, convert all the cards with affected data.
|
|
141
|
+
private async dataTypeChanged() {
|
|
142
|
+
const cardTypesThatUseThisFieldType: string[] = [];
|
|
143
|
+
|
|
144
|
+
// Helper to filter out the unwanted cards.
|
|
145
|
+
function affectedCard(card: Card): boolean {
|
|
146
|
+
if (!card.metadata) return false;
|
|
147
|
+
return cardTypesThatUseThisFieldType.some(
|
|
148
|
+
(item) => item === card.metadata!.cardType,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// First collect the cardTypes that need to be updated.
|
|
153
|
+
const cardTypes = await this.relevantCardTypes(ResourcesFrom.localOnly);
|
|
154
|
+
cardTypesThatUseThisFieldType.push(...cardTypes);
|
|
155
|
+
|
|
156
|
+
// Then collect cards (both project and local template) that use those card types.
|
|
157
|
+
const projectCards = (
|
|
158
|
+
await this.project.cards(this.project.paths.cardRootFolder, {
|
|
159
|
+
metadata: true,
|
|
160
|
+
})
|
|
161
|
+
).filter((card) => affectedCard(card));
|
|
162
|
+
const templateCards = (await this.project.allTemplateCards())
|
|
163
|
+
.filter((card) => !card.path.includes('modules'))
|
|
164
|
+
.filter((card) => affectedCard(card));
|
|
165
|
+
const allCards = [...projectCards, ...templateCards];
|
|
166
|
+
|
|
167
|
+
// Finally, convert values and update the cards.
|
|
168
|
+
allCards.forEach(async (card) => {
|
|
169
|
+
const metadata = card.metadata!;
|
|
170
|
+
const fieldName = resourceNameToString(this.resourceName);
|
|
171
|
+
try {
|
|
172
|
+
metadata[fieldName] = this.convertValue(metadata[fieldName]);
|
|
173
|
+
// Either value was already null, or couldn't convert.
|
|
174
|
+
if (metadata[fieldName] === null) return;
|
|
175
|
+
|
|
176
|
+
await this.project.updateCardMetadata(card, metadata);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error(
|
|
179
|
+
`In card '${card.key}': ${error instanceof Error ? error.message : String(error)}`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Checks that enum with 'enumValue' exists.
|
|
186
|
+
private enumValueExists<Type>(op: Operation<Type>, values: Type[]) {
|
|
187
|
+
const targetValue = (op as Operation<EnumDefinition>).target;
|
|
188
|
+
const foundTarget = values.find(
|
|
189
|
+
(item) => (item as EnumDefinition).enumValue === targetValue.enumValue,
|
|
190
|
+
);
|
|
191
|
+
if (op.name === 'add' && foundTarget) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Cannot perform operation on 'enumValues'. Enum with value '${(op.target as EnumDefinition).enumValue}' already exists`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (op.name === 'remove' && !foundTarget) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Cannot perform operation on 'enumValues'. Enum with value '${(op.target as EnumDefinition).enumValue}' does not exist`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (op.name === 'change') {
|
|
202
|
+
if (!foundTarget) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Cannot perform operation on 'enumValues'. Enum with value '${(op.target as EnumDefinition).enumValue}' does not exist`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const newValue = (op as ChangeOperation<EnumDefinition>).to;
|
|
208
|
+
const foundTo = values.find(
|
|
209
|
+
(item) => (item as EnumDefinition).enumValue === newValue.enumValue,
|
|
210
|
+
);
|
|
211
|
+
if (foundTo) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Cannot perform operation on 'enumValues'. Enum with value '${(op.to as EnumDefinition).enumValue}' already exists`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Return the whole object; caller can just provide 'enumValue'.
|
|
218
|
+
return foundTarget;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// If enum value is removed, and replacement value is given; replace all
|
|
222
|
+
// references to removed enum value with the given replacement value.
|
|
223
|
+
private async handleEnumValueReplacements<Type>(op: Operation<Type>) {
|
|
224
|
+
const removeOp = op as RemoveOperation<Type>;
|
|
225
|
+
const newValue = removeOp.replacementValue as EnumDefinition;
|
|
226
|
+
if (!newValue) return;
|
|
227
|
+
|
|
228
|
+
const removedValue = (op.target as EnumDefinition).enumValue;
|
|
229
|
+
const cardTypes = await this.relevantCardTypes(ResourcesFrom.localOnly);
|
|
230
|
+
const allCards = await Promise.all(
|
|
231
|
+
cardTypes.map((cardType) =>
|
|
232
|
+
this.collectCards({ metadata: true }, cardType),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
const cardsToUpdate = allCards
|
|
236
|
+
.flat()
|
|
237
|
+
.filter((card) => card.metadata?.[this.content.name] === removedValue);
|
|
238
|
+
|
|
239
|
+
await Promise.all(
|
|
240
|
+
cardsToUpdate.map((card) =>
|
|
241
|
+
this.project.updateCardMetadataKey(
|
|
242
|
+
card.key,
|
|
243
|
+
this.content.name,
|
|
244
|
+
newValue.enumValue,
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// When resource name changes.
|
|
251
|
+
private async handleNameChange(existingName: string) {
|
|
252
|
+
await Promise.all([
|
|
253
|
+
super.updateHandleBars(existingName, this.content.name),
|
|
254
|
+
super.updateCalculations(existingName, this.content.name),
|
|
255
|
+
]);
|
|
256
|
+
await this.write();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Checks if value 'from' can be converted 'to' value.
|
|
260
|
+
private isConversionValid(from: DataType, to: DataType) {
|
|
261
|
+
// Set helpers to avoid dragging 'Operation' object everywhere.
|
|
262
|
+
this.fromType = from;
|
|
263
|
+
this.toType = to;
|
|
264
|
+
|
|
265
|
+
return allowed(from, to);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Converts clingo array: "(option1, option2)" => ['option1', 'option2']
|
|
269
|
+
private static parseClingoArray(value: string) {
|
|
270
|
+
const itemsFromParenthesesList = /([^,()]+)/g;
|
|
271
|
+
const results = value.match(itemsFromParenthesesList);
|
|
272
|
+
if (!results) return [];
|
|
273
|
+
return results.map((item) => item.trim());
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Card types that use field type.
|
|
277
|
+
private async relevantCardTypes(from: ResourcesFrom): Promise<string[]> {
|
|
278
|
+
const resourceName = resourceNameToString(this.resourceName);
|
|
279
|
+
const cardTypes = await this.project.cardTypes(from);
|
|
280
|
+
|
|
281
|
+
const references = await Promise.all(
|
|
282
|
+
cardTypes.map(async (cardType) => {
|
|
283
|
+
const metadata = await this.project.resource<CardType>(cardType.name);
|
|
284
|
+
return metadata?.customFields.some(
|
|
285
|
+
(field) => field.name === resourceName,
|
|
286
|
+
)
|
|
287
|
+
? cardType.name
|
|
288
|
+
: '';
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
// Remove empty values.
|
|
292
|
+
return references.filter(Boolean);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Update dependant card types
|
|
296
|
+
private async updateCardTypes(oldName: string) {
|
|
297
|
+
const cardTypes = await this.project.cardTypes(ResourcesFrom.localOnly);
|
|
298
|
+
const op = {
|
|
299
|
+
name: 'change',
|
|
300
|
+
target: oldName,
|
|
301
|
+
to: this.content.name,
|
|
302
|
+
} as ChangeOperation<string>;
|
|
303
|
+
for (const cardType of cardTypes) {
|
|
304
|
+
const object = new CardTypeResource(
|
|
305
|
+
this.project,
|
|
306
|
+
resourceName(cardType.name),
|
|
307
|
+
);
|
|
308
|
+
const data = object.data as CardType;
|
|
309
|
+
if (data) {
|
|
310
|
+
const found = data.customFields
|
|
311
|
+
? data.customFields.find((item) => item.name === oldName)
|
|
312
|
+
: undefined;
|
|
313
|
+
if (found) {
|
|
314
|
+
await object.update('customFields', op);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Creates a new field type object. Base class writes the object to disk automatically.
|
|
322
|
+
* @param dataType Type for the new field type.
|
|
323
|
+
*/
|
|
324
|
+
public async createFieldType(dataType: DataType) {
|
|
325
|
+
if (!FieldTypeResource.fieldDataTypes().includes(dataType)) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Field type '${dataType}' not supported. Supported types ${FieldTypeResource.fieldDataTypes().join(', ')}`,
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const useDataType = dataType;
|
|
332
|
+
const content = DefaultContent.fieldType(
|
|
333
|
+
resourceNameToString(this.resourceName),
|
|
334
|
+
useDataType,
|
|
335
|
+
);
|
|
336
|
+
return super.create(content);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Returns content data.
|
|
341
|
+
*/
|
|
342
|
+
public get data(): FieldType {
|
|
343
|
+
return super.data as FieldType;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Deletes file(s) from disk and clears out the memory resident object.
|
|
348
|
+
*/
|
|
349
|
+
public async delete() {
|
|
350
|
+
return super.delete();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Returns all possible field types.
|
|
355
|
+
* @returns all possible field types.
|
|
356
|
+
*/
|
|
357
|
+
public static fieldDataTypes(): DataType[] {
|
|
358
|
+
return [
|
|
359
|
+
'shortText',
|
|
360
|
+
'longText',
|
|
361
|
+
'number',
|
|
362
|
+
'integer',
|
|
363
|
+
'boolean',
|
|
364
|
+
'enum',
|
|
365
|
+
'list',
|
|
366
|
+
'date',
|
|
367
|
+
'dateTime',
|
|
368
|
+
'person',
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Converts a given 'value' from Clingo result set to a type defined in 'typeName'.
|
|
374
|
+
* @param value Clingo result value (as string) to be converted.
|
|
375
|
+
* @param typeName To which type the 'value' needs to be converted to.
|
|
376
|
+
* @returns converted value.
|
|
377
|
+
*/
|
|
378
|
+
public static fromClingoResult(value: string, typeName: DataType) {
|
|
379
|
+
if (!value) return value;
|
|
380
|
+
if (value === 'null') return JSON.parse(value);
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
switch (typeName) {
|
|
384
|
+
case 'list':
|
|
385
|
+
return FieldTypeResource.parseClingoArray(value);
|
|
386
|
+
case 'boolean':
|
|
387
|
+
return value === 'true';
|
|
388
|
+
case 'date': {
|
|
389
|
+
const date = new Date(value).toISOString();
|
|
390
|
+
return date.substring(0, date.indexOf('T'));
|
|
391
|
+
}
|
|
392
|
+
case 'dateTime':
|
|
393
|
+
return new Date(value).toISOString();
|
|
394
|
+
case 'integer':
|
|
395
|
+
return Math.trunc(Number(value));
|
|
396
|
+
case 'number':
|
|
397
|
+
return Number(value);
|
|
398
|
+
case 'person':
|
|
399
|
+
return EmailValidator.validate(value) ? value : null;
|
|
400
|
+
case 'enum':
|
|
401
|
+
case 'shortText':
|
|
402
|
+
case 'longText':
|
|
403
|
+
default:
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
} catch (error) {
|
|
407
|
+
console.error(
|
|
408
|
+
`Failed to convert value '${value}' to field '${typeName}': ${error instanceof Error ? error.message : String(error)}`,
|
|
409
|
+
);
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Renames resource metadata file and renames memory resident object 'name'.
|
|
416
|
+
* @param newName New name for the resource.
|
|
417
|
+
*/
|
|
418
|
+
public async rename(newName: ResourceName) {
|
|
419
|
+
const existingName = this.content.name;
|
|
420
|
+
await super.rename(newName);
|
|
421
|
+
return this.handleNameChange(existingName);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Shows metadata of the resource.
|
|
426
|
+
* @returns field type metadata.
|
|
427
|
+
*/
|
|
428
|
+
public async show(): Promise<FieldType> {
|
|
429
|
+
return super.show() as Promise<FieldType>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Updates field type resource.
|
|
434
|
+
* @param key Key to modify
|
|
435
|
+
* @param op Operation to perform on 'key'
|
|
436
|
+
*/
|
|
437
|
+
public async update<Type>(key: string, op: Operation<Type>) {
|
|
438
|
+
const nameChange = key === 'name';
|
|
439
|
+
const typeChange = key === 'dataType';
|
|
440
|
+
const enumChange = key === 'enumValues';
|
|
441
|
+
const existingName = this.content.name;
|
|
442
|
+
const existingType = (this.content as FieldType).dataType;
|
|
443
|
+
|
|
444
|
+
await super.update(key, op);
|
|
445
|
+
|
|
446
|
+
const content = this.content as FieldType;
|
|
447
|
+
if (key === 'name') {
|
|
448
|
+
content.name = super.handleScalar(op) as string;
|
|
449
|
+
} else if (key === 'dataType') {
|
|
450
|
+
const toType = op as ChangeOperation<DataType>;
|
|
451
|
+
if (!FieldTypeResource.fieldDataTypes().includes(toType.to)) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Cannot change '${key}' to unknown type '${toType.to}'`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (existingType === toType.to) {
|
|
457
|
+
throw new Error(`'${key}' is already '${toType.to}'`);
|
|
458
|
+
}
|
|
459
|
+
if (!this.isConversionValid(content.dataType, toType.to)) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`Cannot change data type from '${content.dataType}' to '${toType.to}'`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
content.dataType = super.handleScalar(op) as DataType;
|
|
465
|
+
} else if (key === 'displayName') {
|
|
466
|
+
content.displayName = super.handleScalar(op) as string;
|
|
467
|
+
} else if (key === 'enumValues') {
|
|
468
|
+
if (op.name === 'add' || op.name === 'change' || op.name === 'remove') {
|
|
469
|
+
const existingValue = this.enumValueExists<EnumDefinition>(
|
|
470
|
+
op as Operation<EnumDefinition>,
|
|
471
|
+
content.enumValues as EnumDefinition[],
|
|
472
|
+
) as Type;
|
|
473
|
+
op.target = existingValue ?? op.target;
|
|
474
|
+
}
|
|
475
|
+
content.enumValues = super.handleArray(
|
|
476
|
+
op,
|
|
477
|
+
key,
|
|
478
|
+
content.enumValues as Type[],
|
|
479
|
+
) as EnumDefinition[];
|
|
480
|
+
} else if (key === 'fieldDescription') {
|
|
481
|
+
content.fieldDescription = super.handleScalar(op) as string;
|
|
482
|
+
} else {
|
|
483
|
+
throw new Error(`Unknown property '${key}' for FieldType`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
await super.postUpdate(content, key, op);
|
|
487
|
+
|
|
488
|
+
if (nameChange) {
|
|
489
|
+
// Renaming this field type causes that references to its name must be updated.
|
|
490
|
+
await this.handleNameChange(existingName);
|
|
491
|
+
await this.updateCardTypes(existingName);
|
|
492
|
+
} else if (typeChange) {
|
|
493
|
+
await this.dataTypeChanged();
|
|
494
|
+
} else if (enumChange && op.name === 'remove') {
|
|
495
|
+
await this.handleEnumValueReplacements(op);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* List where link type is used.
|
|
501
|
+
* Always returns card key references first, then any resource references and finally calculation references.
|
|
502
|
+
*
|
|
503
|
+
* @param cards Optional. Check these cards for usage of this resource. If undefined, will check all cards.
|
|
504
|
+
* @returns array of card keys, resource names and calculation filenames that refer this resource.
|
|
505
|
+
*/
|
|
506
|
+
public async usage(cards?: Card[]): Promise<string[]> {
|
|
507
|
+
const allCards = cards ?? (await super.cards());
|
|
508
|
+
|
|
509
|
+
const [cardContentReferences, relevantLinkTypes, calculations] =
|
|
510
|
+
await Promise.all([
|
|
511
|
+
super.usage(allCards),
|
|
512
|
+
this.relevantCardTypes(ResourcesFrom.all),
|
|
513
|
+
super.calculations(),
|
|
514
|
+
]);
|
|
515
|
+
|
|
516
|
+
const cardReferences = [
|
|
517
|
+
...this.cardsWithFieldType(allCards),
|
|
518
|
+
...cardContentReferences,
|
|
519
|
+
].sort(sortCards);
|
|
520
|
+
|
|
521
|
+
// Using Set to avoid duplicate cards
|
|
522
|
+
return [
|
|
523
|
+
...new Set([...cardReferences, ...relevantLinkTypes, ...calculations]),
|
|
524
|
+
];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Validates the resource. If object is invalid, throws.
|
|
529
|
+
*/
|
|
530
|
+
public async validate(content?: object) {
|
|
531
|
+
return super.validate(content);
|
|
532
|
+
}
|
|
533
|
+
}
|