@cyberismo/data-handler 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/command-handler.js +5 -7
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.js +4 -4
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.js +1 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/fetch.d.ts +8 -0
- package/dist/commands/fetch.js +101 -23
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/import.d.ts +5 -2
- package/dist/commands/import.js +12 -3
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/remove.d.ts +3 -1
- package/dist/commands/remove.js +7 -1
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.js +5 -0
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +4 -2
- package/dist/commands/show.js +8 -2
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.js +3 -5
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/card-container.d.ts +7 -5
- package/dist/containers/card-container.js +30 -5
- package/dist/containers/card-container.js.map +1 -1
- package/dist/containers/project/project-paths.d.ts +2 -0
- package/dist/containers/project/project-paths.js +6 -0
- package/dist/containers/project/project-paths.js.map +1 -1
- package/dist/containers/project/resource-cache.js +9 -7
- package/dist/containers/project/resource-cache.js.map +1 -1
- package/dist/containers/project.d.ts +11 -2
- package/dist/containers/project.js +54 -8
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.js +4 -4
- package/dist/containers/template.js.map +1 -1
- package/dist/interfaces/command-options.d.ts +3 -1
- package/dist/interfaces/project-interfaces.d.ts +5 -5
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/project-settings.d.ts +5 -0
- package/dist/project-settings.js +12 -0
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +6 -2
- package/dist/resources/card-type-resource.js +68 -86
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +5 -1
- package/dist/resources/field-type-resource.js +49 -54
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +14 -1
- package/dist/resources/file-resource.js +29 -0
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +5 -1
- package/dist/resources/link-type-resource.js +28 -30
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +1 -0
- package/dist/resources/resource-object.js +52 -1
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.js +5 -26
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +1 -1
- package/dist/resources/workflow-resource.js +79 -76
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/configuration-logger.d.ts +91 -0
- package/dist/utils/configuration-logger.js +151 -0
- package/dist/utils/configuration-logger.js.map +1 -0
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +5 -3
- package/dist/utils/constants.js.map +1 -1
- package/package.json +4 -4
- package/src/command-handler.ts +17 -9
- package/src/command-manager.ts +4 -4
- package/src/commands/create.ts +1 -1
- package/src/commands/fetch.ts +143 -34
- package/src/commands/import.ts +13 -1
- package/src/commands/remove.ts +10 -1
- package/src/commands/rename.ts +15 -0
- package/src/commands/show.ts +11 -3
- package/src/commands/validate.ts +3 -7
- package/src/containers/card-container.ts +37 -5
- package/src/containers/project/project-paths.ts +8 -0
- package/src/containers/project/resource-cache.ts +12 -9
- package/src/containers/project.ts +76 -9
- package/src/containers/template.ts +4 -4
- package/src/interfaces/command-options.ts +3 -1
- package/src/interfaces/project-interfaces.ts +5 -5
- package/src/project-settings.ts +13 -0
- package/src/resources/card-type-resource.ts +91 -109
- package/src/resources/field-type-resource.ts +60 -64
- package/src/resources/file-resource.ts +43 -1
- package/src/resources/link-type-resource.ts +32 -36
- package/src/resources/resource-object.ts +73 -1
- package/src/resources/template-resource.ts +6 -26
- package/src/resources/workflow-resource.ts +102 -93
- package/src/utils/configuration-logger.ts +206 -0
- package/src/utils/constants.ts +5 -3
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
unlink,
|
|
22
22
|
writeFile,
|
|
23
23
|
} from 'node:fs/promises';
|
|
24
|
+
import { readdirSync } from 'node:fs';
|
|
24
25
|
|
|
25
26
|
// base class
|
|
26
27
|
import { CardContainer } from './card-container.js';
|
|
@@ -59,6 +60,11 @@ import type { Template } from './template.js';
|
|
|
59
60
|
|
|
60
61
|
import { ROOT } from '../utils/constants.js';
|
|
61
62
|
|
|
63
|
+
import {
|
|
64
|
+
ConfigurationLogger,
|
|
65
|
+
ConfigurationOperation,
|
|
66
|
+
} from '../utils/configuration-logger.js';
|
|
67
|
+
|
|
62
68
|
// Re-export this, so that classes that use Project do not need to have separate import.
|
|
63
69
|
export { ResourcesFrom };
|
|
64
70
|
|
|
@@ -83,6 +89,7 @@ export class Project extends CardContainer {
|
|
|
83
89
|
private resourceWatcher: ContentWatcher | undefined;
|
|
84
90
|
private settings: ProjectConfiguration;
|
|
85
91
|
private validator: Validate;
|
|
92
|
+
private cachedAllModulePrefixes: string[] = [];
|
|
86
93
|
|
|
87
94
|
constructor(
|
|
88
95
|
path: string,
|
|
@@ -95,7 +102,7 @@ export class Project extends CardContainer {
|
|
|
95
102
|
join(path, '.cards', 'local', Project.projectConfigFileName),
|
|
96
103
|
options.autoSave ?? true,
|
|
97
104
|
);
|
|
98
|
-
super(path, settings.cardKeyPrefix
|
|
105
|
+
super(path, settings.cardKeyPrefix);
|
|
99
106
|
this.settings = settings;
|
|
100
107
|
|
|
101
108
|
this.logger.info({ path }, 'Initializing project');
|
|
@@ -103,16 +110,16 @@ export class Project extends CardContainer {
|
|
|
103
110
|
this.calculationEngine = new CalculationEngine(this);
|
|
104
111
|
this.projectPaths = new ProjectPaths(path);
|
|
105
112
|
this.resourceHandler = new ResourceHandler(this);
|
|
106
|
-
|
|
107
|
-
this.containerName = this.settings.name;
|
|
108
113
|
// todo: implement project validation
|
|
109
114
|
this.validator = Validate.getInstance();
|
|
110
115
|
|
|
111
116
|
this.logger.info(
|
|
112
|
-
{ name: this.
|
|
117
|
+
{ name: this.settings.name },
|
|
113
118
|
'Project initialization complete',
|
|
114
119
|
);
|
|
115
120
|
|
|
121
|
+
this.refreshAllModulePrefixes();
|
|
122
|
+
|
|
116
123
|
const ignoreRenameFileChanges = true;
|
|
117
124
|
|
|
118
125
|
// Watch changes in .cards if there are multiple instances of Project being
|
|
@@ -217,12 +224,34 @@ export class Project extends CardContainer {
|
|
|
217
224
|
}
|
|
218
225
|
}
|
|
219
226
|
|
|
227
|
+
// Refreshes the cached list of all module prefixes.
|
|
228
|
+
// This includes both direct and transient module dependencies.
|
|
229
|
+
private refreshAllModulePrefixes(): void {
|
|
230
|
+
const prefixes: string[] = [this.projectPrefix];
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const modules = readdirSync(this.paths.modulesFolder, {
|
|
234
|
+
withFileTypes: true,
|
|
235
|
+
})
|
|
236
|
+
.filter((item) => item.isDirectory())
|
|
237
|
+
.map((item) => item.name);
|
|
238
|
+
|
|
239
|
+
prefixes.push(...modules);
|
|
240
|
+
} catch {
|
|
241
|
+
// If modules folder doesn't exist, fall back to configuration modules only
|
|
242
|
+
const moduleNames = this.configuration.modules.map((item) => item.name);
|
|
243
|
+
prefixes.push(...moduleNames);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this.cachedAllModulePrefixes = prefixes;
|
|
247
|
+
}
|
|
248
|
+
|
|
220
249
|
// Validates that card's data is valid.
|
|
221
250
|
private async validateCard(card: Card): Promise<string> {
|
|
222
251
|
const invalidCustomData = await this.validator.validateCustomFields(
|
|
223
252
|
this,
|
|
224
253
|
card,
|
|
225
|
-
this.
|
|
254
|
+
this.allModulePrefixes(),
|
|
226
255
|
);
|
|
227
256
|
const invalidWorkFlow = await this.validator.validateWorkflowState(
|
|
228
257
|
this,
|
|
@@ -256,7 +285,7 @@ export class Project extends CardContainer {
|
|
|
256
285
|
protected async populateTemplateCards(): Promise<void> {
|
|
257
286
|
try {
|
|
258
287
|
const templateResources = this.resources.templates();
|
|
259
|
-
const prefixes = this.
|
|
288
|
+
const prefixes = this.allModulePrefixes();
|
|
260
289
|
const loadPromises = templateResources.map(async (template) => {
|
|
261
290
|
try {
|
|
262
291
|
this.validator.validResourceName(
|
|
@@ -609,12 +638,30 @@ export class Project extends CardContainer {
|
|
|
609
638
|
/**
|
|
610
639
|
* Adds a module from project.
|
|
611
640
|
* @param module Module to add
|
|
641
|
+
* @param skipMigrationLog If true, skip logging to migration log. Used during project creation.
|
|
612
642
|
*/
|
|
613
|
-
public async importModule(module: ModuleSetting) {
|
|
643
|
+
public async importModule(module: ModuleSetting, skipMigrationLog = false) {
|
|
614
644
|
// Add module as a dependency.
|
|
615
645
|
await this.configuration.addModule(module);
|
|
616
646
|
this.resources.changedModules();
|
|
647
|
+
this.refreshAllModulePrefixes();
|
|
617
648
|
await this.populateTemplateCards();
|
|
649
|
+
|
|
650
|
+
// Log configuration change
|
|
651
|
+
if (!skipMigrationLog) {
|
|
652
|
+
await ConfigurationLogger.log(
|
|
653
|
+
this.basePath,
|
|
654
|
+
ConfigurationOperation.MODULE_ADD,
|
|
655
|
+
module.name,
|
|
656
|
+
{
|
|
657
|
+
parameters: {
|
|
658
|
+
location: module.location,
|
|
659
|
+
branch: module.branch,
|
|
660
|
+
private: module.private,
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
);
|
|
664
|
+
}
|
|
618
665
|
this.logger.info(`Imported module '${module.name}'`);
|
|
619
666
|
}
|
|
620
667
|
|
|
@@ -831,9 +878,18 @@ export class Project extends CardContainer {
|
|
|
831
878
|
}
|
|
832
879
|
|
|
833
880
|
/**
|
|
834
|
-
* Returns all prefixes used in the project
|
|
881
|
+
* Returns all prefixes used in the project.
|
|
882
|
+
* This includes both direct dependencies and transient dependencies.
|
|
835
883
|
* @returns all prefixes used in the project.
|
|
836
884
|
*/
|
|
885
|
+
public allModulePrefixes(): string[] {
|
|
886
|
+
return this.cachedAllModulePrefixes;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Returns prefixes for direct module dependencies only (from cardsConfig.json).
|
|
891
|
+
* @returns prefixes for direct module dependencies.
|
|
892
|
+
*/
|
|
837
893
|
public projectPrefixes(): string[] {
|
|
838
894
|
const prefixes: string[] = [this.projectPrefix];
|
|
839
895
|
const moduleNames = this.configuration.modules.map((item) => item.name);
|
|
@@ -892,6 +948,17 @@ export class Project extends CardContainer {
|
|
|
892
948
|
// Finally, remove module from project configuration
|
|
893
949
|
await this.configuration.removeModule(moduleName);
|
|
894
950
|
|
|
951
|
+
// Refresh cached module prefixes after removal
|
|
952
|
+
this.refreshAllModulePrefixes();
|
|
953
|
+
|
|
954
|
+
// Log configuration change
|
|
955
|
+
await ConfigurationLogger.log(
|
|
956
|
+
this.basePath,
|
|
957
|
+
ConfigurationOperation.MODULE_REMOVE,
|
|
958
|
+
moduleName,
|
|
959
|
+
{},
|
|
960
|
+
);
|
|
961
|
+
|
|
895
962
|
this.logger.info(`Removed module '${moduleName}'`);
|
|
896
963
|
}
|
|
897
964
|
|
|
@@ -909,7 +976,7 @@ export class Project extends CardContainer {
|
|
|
909
976
|
*/
|
|
910
977
|
public async show(): Promise<ProjectMetadata> {
|
|
911
978
|
return {
|
|
912
|
-
name: this.
|
|
979
|
+
name: this.settings.name,
|
|
913
980
|
path: this.basePath,
|
|
914
981
|
prefix: this.projectPrefix,
|
|
915
982
|
hubs: this.configuration.hubs,
|
|
@@ -65,7 +65,7 @@ export class Template extends CardContainer {
|
|
|
65
65
|
constructor(project: Project, template: Resource) {
|
|
66
66
|
// Templates might come from modules. Remove module name from template name.
|
|
67
67
|
const templateName = stripExtension(basename(template.name));
|
|
68
|
-
super(template.path, project.projectPrefix
|
|
68
|
+
super(template.path, project.projectPrefix);
|
|
69
69
|
this.templateName = templateName;
|
|
70
70
|
this.fullTemplateName = template.name;
|
|
71
71
|
|
|
@@ -399,7 +399,7 @@ export class Template extends CardContainer {
|
|
|
399
399
|
try {
|
|
400
400
|
// todo: to use cache instead of file access
|
|
401
401
|
if (!pathExists(this.templateFolder())) {
|
|
402
|
-
throw new Error(`Template '${this.
|
|
402
|
+
throw new Error(`Template '${this.templateName}' does not exist`);
|
|
403
403
|
}
|
|
404
404
|
const cardType = this.project.resources
|
|
405
405
|
.byType(cardTypeName, 'cardTypes')
|
|
@@ -407,7 +407,7 @@ export class Template extends CardContainer {
|
|
|
407
407
|
|
|
408
408
|
if (parentCard && !this.hasTemplateCard(parentCard.key)) {
|
|
409
409
|
throw new Error(
|
|
410
|
-
`Card '${parentCard.key}' does not exist in template '${this.
|
|
410
|
+
`Card '${parentCard.key}' does not exist in template '${this.templateName}'`,
|
|
411
411
|
);
|
|
412
412
|
}
|
|
413
413
|
|
|
@@ -496,7 +496,7 @@ export class Template extends CardContainer {
|
|
|
496
496
|
const cards = this.cards();
|
|
497
497
|
if (cards.length === 0) {
|
|
498
498
|
throw new Error(
|
|
499
|
-
`No cards in template '${this.
|
|
499
|
+
`No cards in template '${this.templateName}'. Please add template cards with 'add' command first.`,
|
|
500
500
|
);
|
|
501
501
|
}
|
|
502
502
|
return this.doCreateCards(cards, parentCard);
|
|
@@ -56,7 +56,9 @@ export interface ExportCommandOptions extends BaseCommandOptions {
|
|
|
56
56
|
export type FetchCommandOptions = BaseCommandOptions;
|
|
57
57
|
|
|
58
58
|
// Options for 'import' command
|
|
59
|
-
export
|
|
59
|
+
export interface ImportCommandOptions extends BaseCommandOptions {
|
|
60
|
+
skipMigrationLog?: boolean;
|
|
61
|
+
}
|
|
60
62
|
|
|
61
63
|
// Options for 'move' command
|
|
62
64
|
export type MoveCommandOptions = BaseCommandOptions;
|
|
@@ -47,13 +47,15 @@ export interface CardListContainer {
|
|
|
47
47
|
}
|
|
48
48
|
// Remember to add all these keys to utils/constants.ts
|
|
49
49
|
export interface PredefinedCardMetadata {
|
|
50
|
-
title: string;
|
|
51
50
|
cardType: string;
|
|
52
|
-
|
|
53
|
-
rank: string;
|
|
51
|
+
labels?: string[];
|
|
54
52
|
lastTransitioned?: string;
|
|
55
53
|
lastUpdated?: string;
|
|
54
|
+
links: Link[];
|
|
55
|
+
rank: string;
|
|
56
56
|
templateCardKey?: string;
|
|
57
|
+
title: string;
|
|
58
|
+
workflowState: string;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
// todo: do we need in the future separation between module-template-cards and local template-cards
|
|
@@ -65,8 +67,6 @@ export enum CardLocation {
|
|
|
65
67
|
|
|
66
68
|
// Card's index.json file content.
|
|
67
69
|
export interface CardMetadata extends PredefinedCardMetadata {
|
|
68
|
-
labels?: string[];
|
|
69
|
-
links: Link[];
|
|
70
70
|
[key: string]: MetadataContent;
|
|
71
71
|
}
|
|
72
72
|
|
package/src/project-settings.ts
CHANGED
|
@@ -265,4 +265,17 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
265
265
|
`Prefix '${newPrefix}' is not valid prefix. Prefix should be in lowercase and contain letters from a to z (max length 10).`,
|
|
266
266
|
);
|
|
267
267
|
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Changes project name.
|
|
271
|
+
* @param newName New project name
|
|
272
|
+
*/
|
|
273
|
+
public async setProjectName(newName: string) {
|
|
274
|
+
const isValid = Validate.isValidProjectName(newName);
|
|
275
|
+
if (isValid) {
|
|
276
|
+
this.name = newName;
|
|
277
|
+
return this.save();
|
|
278
|
+
}
|
|
279
|
+
throw new Error(`Project name '${newName}' is not valid.`);
|
|
280
|
+
}
|
|
268
281
|
}
|
|
@@ -102,38 +102,6 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
// When resource name changes.
|
|
106
|
-
private async handleNameChange(existingName: string) {
|
|
107
|
-
const current = this.content;
|
|
108
|
-
const prefixes = this.project.projectPrefixes();
|
|
109
|
-
if (current.customFields) {
|
|
110
|
-
current.customFields.map(
|
|
111
|
-
(field) =>
|
|
112
|
-
(field.name = this.updatePrefixInResourceName(field.name, prefixes)),
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
if (current.alwaysVisibleFields) {
|
|
116
|
-
current.alwaysVisibleFields = current.alwaysVisibleFields.map((item) =>
|
|
117
|
-
this.updatePrefixInResourceName(item, prefixes),
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
if (current.optionallyVisibleFields) {
|
|
121
|
-
current.optionallyVisibleFields = current.optionallyVisibleFields.map(
|
|
122
|
-
(item) => this.updatePrefixInResourceName(item, prefixes),
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
current.workflow = this.updatePrefixInResourceName(
|
|
126
|
-
current.workflow,
|
|
127
|
-
prefixes,
|
|
128
|
-
);
|
|
129
|
-
await Promise.all([
|
|
130
|
-
super.updateHandleBars(existingName, this.content.name),
|
|
131
|
-
super.updateCalculations(existingName, this.content.name),
|
|
132
|
-
]);
|
|
133
|
-
// Finally, write updated content.
|
|
134
|
-
await this.write();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
105
|
// When a field is removed, remove it from all affected cards.
|
|
138
106
|
private async handleRemoveField(cards: Card[], item: CustomField) {
|
|
139
107
|
for (const card of cards) {
|
|
@@ -189,15 +157,6 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
189
157
|
);
|
|
190
158
|
}
|
|
191
159
|
|
|
192
|
-
// Remove value from array.
|
|
193
|
-
// todo: make it as generic and move to utils
|
|
194
|
-
private removeValue(array: string[], value: string) {
|
|
195
|
-
const index = array.findIndex((element) => element === value);
|
|
196
|
-
if (index !== -1) {
|
|
197
|
-
array.splice(index, 1);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
160
|
// Return link types that use this card type.
|
|
202
161
|
private relevantLinkTypes(): string[] {
|
|
203
162
|
const resourceName = resourceNameToString(this.resourceName);
|
|
@@ -220,6 +179,15 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
220
179
|
return references;
|
|
221
180
|
}
|
|
222
181
|
|
|
182
|
+
// Remove value from array.
|
|
183
|
+
// todo: make it as generic and move to utils
|
|
184
|
+
private removeValue(array: string[], value: string) {
|
|
185
|
+
const index = array.findIndex((element) => element === value);
|
|
186
|
+
if (index !== -1) {
|
|
187
|
+
array.splice(index, 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
223
191
|
// If value from 'customFields' is removed, remove it also from 'optionallyVisible' and 'alwaysVisible' arrays.
|
|
224
192
|
private removeValueFromOtherArrays<Type>(
|
|
225
193
|
op: Operation<Type>,
|
|
@@ -246,21 +214,8 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
246
214
|
if (item.isCalculated === undefined) {
|
|
247
215
|
item.isCalculated = false;
|
|
248
216
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const fieldType = this.project.resources.byType(
|
|
252
|
-
item.name,
|
|
253
|
-
'fieldTypes',
|
|
254
|
-
);
|
|
255
|
-
const fieldTypeContent = fieldType.data;
|
|
256
|
-
if (fieldTypeContent) {
|
|
257
|
-
item.displayName = fieldTypeContent.displayName;
|
|
258
|
-
}
|
|
259
|
-
} else if (!item.name) {
|
|
260
|
-
console.error(
|
|
261
|
-
`Custom field '${item.name}' is missing mandatory 'name' in card type '${content.name}'`,
|
|
262
|
-
);
|
|
263
|
-
return undefined;
|
|
217
|
+
if (!item.name) {
|
|
218
|
+
continue;
|
|
264
219
|
}
|
|
265
220
|
}
|
|
266
221
|
} else {
|
|
@@ -400,6 +355,42 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
400
355
|
}
|
|
401
356
|
}
|
|
402
357
|
|
|
358
|
+
/**
|
|
359
|
+
* When resource name changes
|
|
360
|
+
* @param existingName Current resource name
|
|
361
|
+
*/
|
|
362
|
+
protected async onNameChange(existingName: string) {
|
|
363
|
+
const current = this.content;
|
|
364
|
+
const prefixes = this.project.projectPrefixes();
|
|
365
|
+
if (current.customFields) {
|
|
366
|
+
current.customFields.map(
|
|
367
|
+
(field) =>
|
|
368
|
+
(field.name = this.updatePrefixInResourceName(field.name, prefixes)),
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
if (current.alwaysVisibleFields) {
|
|
372
|
+
current.alwaysVisibleFields = current.alwaysVisibleFields.map((item) =>
|
|
373
|
+
this.updatePrefixInResourceName(item, prefixes),
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
if (current.optionallyVisibleFields) {
|
|
377
|
+
current.optionallyVisibleFields = current.optionallyVisibleFields.map(
|
|
378
|
+
(item) => this.updatePrefixInResourceName(item, prefixes),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
current.workflow = this.updatePrefixInResourceName(
|
|
382
|
+
current.workflow,
|
|
383
|
+
prefixes,
|
|
384
|
+
);
|
|
385
|
+
await Promise.all([
|
|
386
|
+
super.updateHandleBars(existingName, this.content.name),
|
|
387
|
+
super.updateCalculations(existingName, this.content.name),
|
|
388
|
+
this.updateLinkTypes(existingName),
|
|
389
|
+
]);
|
|
390
|
+
|
|
391
|
+
await this.write();
|
|
392
|
+
}
|
|
393
|
+
|
|
403
394
|
/**
|
|
404
395
|
* Creates a new card type object. Base class writes the object to disk automatically.
|
|
405
396
|
* @param workflowName Workflow name that this card type uses.
|
|
@@ -438,7 +429,7 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
438
429
|
public async rename(newName: ResourceName) {
|
|
439
430
|
const existingName = this.content.name;
|
|
440
431
|
await super.rename(newName);
|
|
441
|
-
return this.
|
|
432
|
+
return this.onNameChange(existingName);
|
|
442
433
|
}
|
|
443
434
|
|
|
444
435
|
/**
|
|
@@ -451,60 +442,51 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
451
442
|
op: Operation<Type>,
|
|
452
443
|
) {
|
|
453
444
|
const { key } = updateKey;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
445
|
+
|
|
446
|
+
if (key === 'name' || key === 'description' || key === 'displayName') {
|
|
447
|
+
await super.update(updateKey, op);
|
|
448
|
+
} else {
|
|
449
|
+
const content = structuredClone(this.content);
|
|
450
|
+
const customFieldsChange = key === 'customFields';
|
|
451
|
+
if (key === 'alwaysVisibleFields') {
|
|
452
|
+
await this.validateFieldType(key, op);
|
|
453
|
+
content.alwaysVisibleFields = super.handleArray(
|
|
454
|
+
op,
|
|
455
|
+
key,
|
|
456
|
+
content.alwaysVisibleFields as Type[],
|
|
457
|
+
) as string[];
|
|
458
|
+
} else if (key === 'optionallyVisibleFields') {
|
|
459
|
+
await this.validateFieldType(key, op);
|
|
460
|
+
content.optionallyVisibleFields = super.handleArray(
|
|
461
|
+
op,
|
|
462
|
+
key,
|
|
463
|
+
content.optionallyVisibleFields as Type[],
|
|
464
|
+
) as string[];
|
|
465
|
+
} else if (key === 'workflow') {
|
|
466
|
+
const changeOp = op as ChangeOperation<string>;
|
|
467
|
+
const stateMapping = changeOp.mappingTable?.stateMapping || {};
|
|
468
|
+
content.workflow = super.handleScalar(op) as string;
|
|
469
|
+
if (Object.keys(stateMapping).length > 0) {
|
|
470
|
+
await this.handleWorkflowChange(stateMapping, changeOp);
|
|
471
|
+
}
|
|
472
|
+
} else if (key === 'customFields') {
|
|
473
|
+
await this.validateFieldType(key, op);
|
|
474
|
+
content.customFields = super.handleArray(
|
|
475
|
+
op,
|
|
476
|
+
key,
|
|
477
|
+
content.customFields as Type[],
|
|
478
|
+
) as CustomField[];
|
|
479
|
+
if (op.name === 'remove') {
|
|
480
|
+
this.removeValueFromOtherArrays(op, content);
|
|
481
|
+
}
|
|
482
|
+
} else {
|
|
483
|
+
throw new Error(`Unknown property '${key}' for CardType`);
|
|
482
484
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
op
|
|
487
|
-
key,
|
|
488
|
-
content.customFields as Type[],
|
|
489
|
-
) as CustomField[];
|
|
490
|
-
if (op.name === 'remove') {
|
|
491
|
-
this.removeValueFromOtherArrays(op, content);
|
|
485
|
+
await super.postUpdate(content, updateKey, op);
|
|
486
|
+
|
|
487
|
+
if (customFieldsChange) {
|
|
488
|
+
return this.handleCustomFieldsChange(op as ChangeOperation<string>);
|
|
492
489
|
}
|
|
493
|
-
} else if (key === 'description') {
|
|
494
|
-
content.description = super.handleScalar(op) as string;
|
|
495
|
-
} else if (key === 'displayName') {
|
|
496
|
-
content.displayName = super.handleScalar(op) as string;
|
|
497
|
-
} else {
|
|
498
|
-
throw new Error(`Unknown property '${key}' for CardType`);
|
|
499
|
-
}
|
|
500
|
-
await super.postUpdate(content, updateKey, op);
|
|
501
|
-
|
|
502
|
-
// Renaming this card type causes that references to its name must be updated.
|
|
503
|
-
if (nameChange) {
|
|
504
|
-
await this.handleNameChange(existingName);
|
|
505
|
-
await this.updateLinkTypes(existingName);
|
|
506
|
-
} else if (customFieldsChange) {
|
|
507
|
-
return this.handleCustomFieldsChange(op as ChangeOperation<string>);
|
|
508
490
|
}
|
|
509
491
|
}
|
|
510
492
|
|
|
@@ -223,15 +223,6 @@ export class FieldTypeResource extends FileResource<FieldType> {
|
|
|
223
223
|
);
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
// When resource name changes.
|
|
227
|
-
private async handleNameChange(existingName: string) {
|
|
228
|
-
await Promise.all([
|
|
229
|
-
super.updateHandleBars(existingName, this.content.name),
|
|
230
|
-
super.updateCalculations(existingName, this.content.name),
|
|
231
|
-
]);
|
|
232
|
-
await this.write();
|
|
233
|
-
}
|
|
234
|
-
|
|
235
226
|
// Checks if value 'from' can be converted 'to' value.
|
|
236
227
|
private isConversionValid(from: DataType, to: DataType) {
|
|
237
228
|
// Set helpers to avoid dragging 'Operation' object everywhere.
|
|
@@ -284,6 +275,19 @@ export class FieldTypeResource extends FileResource<FieldType> {
|
|
|
284
275
|
}
|
|
285
276
|
}
|
|
286
277
|
|
|
278
|
+
/**
|
|
279
|
+
* When resource name changes.
|
|
280
|
+
* @param existingName Current resource name.
|
|
281
|
+
*/
|
|
282
|
+
protected async onNameChange(existingName: string) {
|
|
283
|
+
await Promise.all([
|
|
284
|
+
super.updateHandleBars(existingName, this.content.name),
|
|
285
|
+
super.updateCalculations(existingName, this.content.name),
|
|
286
|
+
this.updateCardTypes(existingName),
|
|
287
|
+
]);
|
|
288
|
+
await this.write();
|
|
289
|
+
}
|
|
290
|
+
|
|
287
291
|
/**
|
|
288
292
|
* Creates a new field type object. Base class writes the object to disk automatically.
|
|
289
293
|
* @param dataType Type for the new field type.
|
|
@@ -372,7 +376,7 @@ export class FieldTypeResource extends FileResource<FieldType> {
|
|
|
372
376
|
public async rename(newName: ResourceName) {
|
|
373
377
|
const existingName = this.content.name;
|
|
374
378
|
await super.rename(newName);
|
|
375
|
-
return this.
|
|
379
|
+
return this.onNameChange(existingName);
|
|
376
380
|
}
|
|
377
381
|
|
|
378
382
|
/**
|
|
@@ -389,64 +393,56 @@ export class FieldTypeResource extends FileResource<FieldType> {
|
|
|
389
393
|
op: Operation<Type>,
|
|
390
394
|
) {
|
|
391
395
|
const { key } = updateKey;
|
|
392
|
-
const nameChange = key === 'name';
|
|
393
|
-
const typeChange = key === 'dataType';
|
|
394
|
-
const enumChange = key === 'enumValues';
|
|
395
|
-
const existingName = this.content.name;
|
|
396
|
-
const existingType = this.content.dataType;
|
|
397
396
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const content = structuredClone(this.content);
|
|
401
|
-
if (key === 'name') {
|
|
402
|
-
content.name = super.handleScalar(op) as string;
|
|
403
|
-
} else if (key === 'dataType') {
|
|
404
|
-
const toType = op as ChangeOperation<DataType>;
|
|
405
|
-
if (!FieldTypeResource.fieldDataTypes().includes(toType.to)) {
|
|
406
|
-
throw new Error(
|
|
407
|
-
`Cannot change '${key}' to unknown type '${toType.to}'`,
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
if (existingType === toType.to) {
|
|
411
|
-
throw new Error(`'${key}' is already '${toType.to}'`);
|
|
412
|
-
}
|
|
413
|
-
if (!this.isConversionValid(content.dataType, toType.to)) {
|
|
414
|
-
throw new Error(
|
|
415
|
-
`Cannot change data type from '${content.dataType}' to '${toType.to}'`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
content.dataType = super.handleScalar(op) as DataType;
|
|
419
|
-
} else if (key === 'displayName') {
|
|
420
|
-
content.displayName = super.handleScalar(op) as string;
|
|
421
|
-
} else if (key === 'enumValues') {
|
|
422
|
-
if (op.name === 'add' || op.name === 'change' || op.name === 'remove') {
|
|
423
|
-
const existingValue = this.enumValueExists<EnumDefinition>(
|
|
424
|
-
op as Operation<EnumDefinition>,
|
|
425
|
-
content.enumValues as EnumDefinition[],
|
|
426
|
-
) as Type;
|
|
427
|
-
op.target = existingValue ?? op.target;
|
|
428
|
-
}
|
|
429
|
-
content.enumValues = super.handleArray(
|
|
430
|
-
op,
|
|
431
|
-
key,
|
|
432
|
-
content.enumValues as Type[],
|
|
433
|
-
) as EnumDefinition[];
|
|
434
|
-
} else if (key === 'description') {
|
|
435
|
-
content.description = super.handleScalar(op) as string;
|
|
397
|
+
if (key === 'name' || key === 'description' || key === 'displayName') {
|
|
398
|
+
await super.update(updateKey, op);
|
|
436
399
|
} else {
|
|
437
|
-
|
|
438
|
-
|
|
400
|
+
const content = structuredClone(this.content);
|
|
401
|
+
const typeChange = key === 'dataType';
|
|
402
|
+
const enumChange = key === 'enumValues';
|
|
403
|
+
const existingType = this.content.dataType;
|
|
404
|
+
if (key === 'name') {
|
|
405
|
+
content.name = super.handleScalar(op) as string;
|
|
406
|
+
} else if (key === 'dataType') {
|
|
407
|
+
const toType = op as ChangeOperation<DataType>;
|
|
408
|
+
if (!FieldTypeResource.fieldDataTypes().includes(toType.to)) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Cannot change '${key}' to unknown type '${toType.to}'`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
if (existingType === toType.to) {
|
|
414
|
+
throw new Error(`'${key}' is already '${toType.to}'`);
|
|
415
|
+
}
|
|
416
|
+
if (!this.isConversionValid(content.dataType, toType.to)) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
`Cannot change data type from '${content.dataType}' to '${toType.to}'`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
content.dataType = super.handleScalar(op) as DataType;
|
|
422
|
+
} else if (key === 'enumValues') {
|
|
423
|
+
if (op.name === 'add' || op.name === 'change' || op.name === 'remove') {
|
|
424
|
+
const existingValue = this.enumValueExists<EnumDefinition>(
|
|
425
|
+
op as Operation<EnumDefinition>,
|
|
426
|
+
content.enumValues as EnumDefinition[],
|
|
427
|
+
) as Type;
|
|
428
|
+
op.target = existingValue ?? op.target;
|
|
429
|
+
}
|
|
430
|
+
content.enumValues = super.handleArray(
|
|
431
|
+
op,
|
|
432
|
+
key,
|
|
433
|
+
content.enumValues as Type[],
|
|
434
|
+
) as EnumDefinition[];
|
|
435
|
+
} else {
|
|
436
|
+
throw new Error(`Unknown property '${key}' for FieldType`);
|
|
437
|
+
}
|
|
439
438
|
|
|
440
|
-
|
|
439
|
+
await super.postUpdate(content, updateKey, op);
|
|
441
440
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
await this.dataTypeChanged();
|
|
448
|
-
} else if (enumChange && op.name === 'remove') {
|
|
449
|
-
await this.handleEnumValueReplacements(op);
|
|
441
|
+
if (typeChange) {
|
|
442
|
+
await this.dataTypeChanged();
|
|
443
|
+
} else if (enumChange && op.name === 'remove') {
|
|
444
|
+
await this.handleEnumValueReplacements(op);
|
|
445
|
+
}
|
|
450
446
|
}
|
|
451
447
|
}
|
|
452
448
|
|