@cyberismo/data-handler 0.0.17 → 0.0.19
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.d.ts +2 -0
- package/dist/command-handler.js +26 -2
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +2 -0
- package/dist/command-manager.js +3 -0
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/create.d.ts +3 -1
- package/dist/commands/create.js +10 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/migrate.d.ts +33 -0
- package/dist/commands/migrate.js +66 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/containers/project/card-cache.js +13 -1
- package/dist/containers/project/card-cache.js.map +1 -1
- package/dist/containers/project/project-paths.d.ts +1 -0
- package/dist/containers/project/project-paths.js +5 -2
- package/dist/containers/project/project-paths.js.map +1 -1
- package/dist/containers/project.d.ts +10 -0
- package/dist/containers/project.js +39 -2
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.js +2 -0
- package/dist/containers/template.js.map +1 -1
- package/dist/interfaces/command-options.d.ts +6 -1
- package/dist/interfaces/project-interfaces.d.ts +4 -0
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/migrations/index.d.ts +14 -0
- package/dist/migrations/index.js +14 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/migration-executor.d.ts +79 -0
- package/dist/migrations/migration-executor.js +312 -0
- package/dist/migrations/migration-executor.js.map +1 -0
- package/dist/migrations/migration-worker.d.ts +13 -0
- package/dist/migrations/migration-worker.js +156 -0
- package/dist/migrations/migration-worker.js.map +1 -0
- package/dist/migrations/worker-executor.d.ts +24 -0
- package/dist/migrations/worker-executor.js +157 -0
- package/dist/migrations/worker-executor.js.map +1 -0
- package/dist/project-settings.d.ts +2 -0
- package/dist/project-settings.js +7 -0
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/calculation-resource.d.ts +9 -0
- package/dist/resources/calculation-resource.js +13 -2
- package/dist/resources/calculation-resource.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +12 -3
- package/dist/resources/card-type-resource.js +73 -91
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +10 -1
- package/dist/resources/field-type-resource.js +62 -61
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +27 -2
- package/dist/resources/file-resource.js +46 -8
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +5 -0
- package/dist/resources/graph-model-resource.js +6 -0
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +5 -0
- package/dist/resources/graph-view-resource.js +6 -0
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.d.ts +11 -1
- package/dist/resources/link-type-resource.js +54 -30
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +6 -1
- package/dist/resources/report-resource.js +7 -1
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +22 -7
- package/dist/resources/resource-object.js +44 -15
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +5 -1
- package/dist/resources/template-resource.js +11 -27
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +7 -3
- package/dist/resources/workflow-resource.js +90 -82
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/card-utils.d.ts +1 -1
- package/dist/utils/common-utils.d.ts +8 -0
- package/dist/utils/common-utils.js +14 -0
- package/dist/utils/common-utils.js.map +1 -1
- package/dist/utils/file-utils.d.ts +15 -3
- package/dist/utils/file-utils.js +48 -9
- package/dist/utils/file-utils.js.map +1 -1
- package/dist/utils/json.js +2 -2
- package/dist/utils/json.js.map +1 -1
- package/package.json +5 -3
- package/src/command-handler.ts +38 -1
- package/src/command-manager.ts +3 -0
- package/src/commands/create.ts +11 -0
- package/src/commands/migrate.ts +88 -0
- package/src/containers/project/card-cache.ts +18 -1
- package/src/containers/project/project-paths.ts +6 -2
- package/src/containers/project.ts +66 -1
- package/src/containers/template.ts +5 -0
- package/src/interfaces/command-options.ts +8 -0
- package/src/interfaces/project-interfaces.ts +4 -0
- package/src/migrations/index.ts +20 -0
- package/src/migrations/migration-executor.ts +478 -0
- package/src/migrations/migration-worker.ts +190 -0
- package/src/migrations/worker-executor.ts +185 -0
- package/src/project-settings.ts +7 -0
- package/src/resources/calculation-resource.ts +13 -2
- package/src/resources/card-type-resource.ts +101 -114
- package/src/resources/field-type-resource.ts +78 -71
- package/src/resources/file-resource.ts +68 -9
- package/src/resources/graph-model-resource.ts +6 -0
- package/src/resources/graph-view-resource.ts +6 -0
- package/src/resources/link-type-resource.ts +66 -36
- package/src/resources/report-resource.ts +7 -1
- package/src/resources/resource-object.ts +57 -18
- package/src/resources/template-resource.ts +12 -27
- package/src/resources/workflow-resource.ts +119 -100
- package/src/utils/common-utils.ts +15 -0
- package/src/utils/file-utils.ts +56 -12
- package/src/utils/json.ts +2 -6
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
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 { dirname, join } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { Worker } from 'node:worker_threads';
|
|
17
|
+
|
|
18
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
19
|
+
import type {
|
|
20
|
+
MigrationContext,
|
|
21
|
+
MigrationStepResult,
|
|
22
|
+
} from '@cyberismo/migrations';
|
|
23
|
+
import type { WorkerMessage, WorkerResponse } from './migration-executor.js';
|
|
24
|
+
|
|
25
|
+
const CANCEL_PERIOD_MS = 100;
|
|
26
|
+
const logger = getChildLogger({ module: 'WorkerExecutor' });
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Execute a migration step in a separate worker thread.
|
|
30
|
+
* The worker loads the migration program dynamically and executes the specified step.
|
|
31
|
+
*
|
|
32
|
+
* @param migrationPath Absolute path to the migration's 'index.js' file
|
|
33
|
+
* @param stepName The migration step to execute
|
|
34
|
+
* @param context Migration context
|
|
35
|
+
* @param timeoutMilliSeconds Timeout in milliseconds to wait for the step to complete
|
|
36
|
+
* @returns Migration step result
|
|
37
|
+
*/
|
|
38
|
+
export async function executeStep(
|
|
39
|
+
migrationPath: string,
|
|
40
|
+
stepName: string,
|
|
41
|
+
context: MigrationContext,
|
|
42
|
+
timeoutMilliSeconds: number,
|
|
43
|
+
): Promise<MigrationStepResult> {
|
|
44
|
+
// Always uses the compiled .js version from the dist directory.
|
|
45
|
+
function _workerPath() {
|
|
46
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
47
|
+
const currentDir = dirname(currentFilePath);
|
|
48
|
+
const srcMigrationsSegment = join('src', 'migrations');
|
|
49
|
+
const distMigrationsSegment = join('dist', 'migrations');
|
|
50
|
+
const distDir = currentDir.replace(
|
|
51
|
+
srcMigrationsSegment,
|
|
52
|
+
distMigrationsSegment,
|
|
53
|
+
);
|
|
54
|
+
return join(distDir, 'migration-worker.js');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const worker = new Worker(_workerPath(), {
|
|
59
|
+
execArgv: process.execArgv,
|
|
60
|
+
});
|
|
61
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
62
|
+
let isResolved = false;
|
|
63
|
+
|
|
64
|
+
const cleanup = () => {
|
|
65
|
+
if (timeoutId) {
|
|
66
|
+
clearTimeout(timeoutId);
|
|
67
|
+
timeoutId = undefined;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const terminate = async (sendCancel: boolean = false): Promise<void> => {
|
|
72
|
+
if (sendCancel) {
|
|
73
|
+
try {
|
|
74
|
+
const cancelMessage: WorkerMessage = { type: 'cancel' };
|
|
75
|
+
worker.postMessage(cancelMessage);
|
|
76
|
+
await new Promise((_resolve) =>
|
|
77
|
+
setTimeout(_resolve, CANCEL_PERIOD_MS),
|
|
78
|
+
);
|
|
79
|
+
} catch {
|
|
80
|
+
// Ignore errors when sending cancel message
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await worker.terminate();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.debug({ error }, 'Error terminating worker');
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
timeoutId = setTimeout(() => {
|
|
92
|
+
if (!isResolved) {
|
|
93
|
+
isResolved = true;
|
|
94
|
+
cleanup();
|
|
95
|
+
|
|
96
|
+
void (async () => {
|
|
97
|
+
await terminate(true);
|
|
98
|
+
resolve({
|
|
99
|
+
success: false,
|
|
100
|
+
error: new Error(`Migration step '${stepName}' timeout`),
|
|
101
|
+
});
|
|
102
|
+
})();
|
|
103
|
+
}
|
|
104
|
+
}, timeoutMilliSeconds);
|
|
105
|
+
|
|
106
|
+
worker.on('message', (response: WorkerResponse) => {
|
|
107
|
+
if (isResolved) return;
|
|
108
|
+
|
|
109
|
+
isResolved = true;
|
|
110
|
+
cleanup();
|
|
111
|
+
|
|
112
|
+
void (async () => {
|
|
113
|
+
if (response.type === 'error') {
|
|
114
|
+
await terminate(false);
|
|
115
|
+
resolve({
|
|
116
|
+
success: false,
|
|
117
|
+
error: new Error(response.error || 'Unknown worker error'),
|
|
118
|
+
});
|
|
119
|
+
} else if (response.type === 'result' && response.result) {
|
|
120
|
+
await terminate(false);
|
|
121
|
+
resolve(response.result);
|
|
122
|
+
} else {
|
|
123
|
+
await terminate(false);
|
|
124
|
+
resolve({
|
|
125
|
+
success: false,
|
|
126
|
+
error: new Error('Invalid worker response'),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
worker.on('error', (error) => {
|
|
133
|
+
if (isResolved) return;
|
|
134
|
+
|
|
135
|
+
isResolved = true;
|
|
136
|
+
cleanup();
|
|
137
|
+
|
|
138
|
+
void (async () => {
|
|
139
|
+
await terminate(false);
|
|
140
|
+
resolve({
|
|
141
|
+
success: false,
|
|
142
|
+
error,
|
|
143
|
+
});
|
|
144
|
+
})();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
worker.on('exit', (code) => {
|
|
148
|
+
if (isResolved) return;
|
|
149
|
+
|
|
150
|
+
isResolved = true;
|
|
151
|
+
cleanup();
|
|
152
|
+
|
|
153
|
+
if (code !== 0) {
|
|
154
|
+
resolve({
|
|
155
|
+
success: false,
|
|
156
|
+
error: new Error(`Worker exited with code ${code}`),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const migrationContext: MigrationContext = {
|
|
162
|
+
cardRootPath: context.cardRootPath,
|
|
163
|
+
cardsConfigPath: context.cardsConfigPath,
|
|
164
|
+
fromVersion: context.fromVersion,
|
|
165
|
+
toVersion: context.toVersion,
|
|
166
|
+
backupDir: context.backupDir,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const executeMessage: WorkerMessage = {
|
|
170
|
+
type: 'execute',
|
|
171
|
+
migrationPath,
|
|
172
|
+
stepName,
|
|
173
|
+
context: migrationContext,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
worker.postMessage(executeMessage);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
isResolved = true;
|
|
180
|
+
cleanup();
|
|
181
|
+
void terminate(false);
|
|
182
|
+
reject(error);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
package/src/project-settings.ts
CHANGED
|
@@ -35,6 +35,8 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
35
35
|
schemaVersion?: number;
|
|
36
36
|
name: string;
|
|
37
37
|
cardKeyPrefix: string;
|
|
38
|
+
category?: string;
|
|
39
|
+
description: string;
|
|
38
40
|
modules: ModuleSetting[];
|
|
39
41
|
hubs: HubSetting[];
|
|
40
42
|
private logger = getChildLogger({ module: 'Project' });
|
|
@@ -45,6 +47,7 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
45
47
|
this.name = '';
|
|
46
48
|
this.settingPath = path;
|
|
47
49
|
this.cardKeyPrefix = '';
|
|
50
|
+
this.description = '';
|
|
48
51
|
this.modules = [];
|
|
49
52
|
this.hubs = [];
|
|
50
53
|
this.autoSave = autoSave;
|
|
@@ -79,6 +82,8 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
79
82
|
this.schemaVersion = settings.schemaVersion;
|
|
80
83
|
this.cardKeyPrefix = settings.cardKeyPrefix;
|
|
81
84
|
this.name = settings.name;
|
|
85
|
+
this.category = settings.category;
|
|
86
|
+
this.description = settings.description || '';
|
|
82
87
|
this.modules = settings.modules || [];
|
|
83
88
|
this.hubs = settings.hubs || [];
|
|
84
89
|
} else {
|
|
@@ -106,6 +111,8 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
106
111
|
schemaVersion: this.schemaVersion,
|
|
107
112
|
cardKeyPrefix: this.cardKeyPrefix,
|
|
108
113
|
name: this.name,
|
|
114
|
+
category: this.category,
|
|
115
|
+
description: this.description,
|
|
109
116
|
modules: this.modules,
|
|
110
117
|
hubs: this.hubs,
|
|
111
118
|
};
|
|
@@ -34,6 +34,11 @@ export class CalculationResource extends FolderResource<
|
|
|
34
34
|
CalculationMetadata,
|
|
35
35
|
CalculationContent
|
|
36
36
|
> {
|
|
37
|
+
/**
|
|
38
|
+
* Creates instance of CalculationResource
|
|
39
|
+
* @param project Project to use
|
|
40
|
+
* @param name Resource name
|
|
41
|
+
*/
|
|
37
42
|
constructor(project: Project, name: ResourceName) {
|
|
38
43
|
super(project, name, 'calculations');
|
|
39
44
|
|
|
@@ -41,9 +46,15 @@ export class CalculationResource extends FolderResource<
|
|
|
41
46
|
this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
/**
|
|
50
|
+
* When resource name changes
|
|
51
|
+
* @param existingName Current resource name
|
|
52
|
+
*/
|
|
45
53
|
protected async onNameChange(existingName: string) {
|
|
46
|
-
await
|
|
54
|
+
await Promise.all([
|
|
55
|
+
super.updateCalculations(existingName, this.content.name),
|
|
56
|
+
super.updateCardContentReferences(existingName, this.content.name),
|
|
57
|
+
]);
|
|
47
58
|
await this.write();
|
|
48
59
|
}
|
|
49
60
|
|
|
@@ -17,6 +17,7 @@ import { FileResource } from './file-resource.js';
|
|
|
17
17
|
import { resourceName, resourceNameToString } from '../utils/resource-utils.js';
|
|
18
18
|
import { ResourcesFrom } from '../containers/project.js';
|
|
19
19
|
import { sortCards } from '../utils/card-utils.js';
|
|
20
|
+
import { removeValue } from '../utils/common-utils.js';
|
|
20
21
|
import { Validate } from '../commands/validate.js';
|
|
21
22
|
|
|
22
23
|
import type {
|
|
@@ -39,6 +40,11 @@ import type { ResourceName } from '../utils/resource-utils.js';
|
|
|
39
40
|
* Card type resource class.
|
|
40
41
|
*/
|
|
41
42
|
export class CardTypeResource extends FileResource<CardType> {
|
|
43
|
+
/**
|
|
44
|
+
* Creates instance of CardTypeResource
|
|
45
|
+
* @param project Project to use
|
|
46
|
+
* @param name Resource name
|
|
47
|
+
*/
|
|
42
48
|
constructor(project: Project, name: ResourceName) {
|
|
43
49
|
super(project, name, 'cardTypes');
|
|
44
50
|
|
|
@@ -69,7 +75,10 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
69
75
|
if (op && op.name === 'rank') return;
|
|
70
76
|
|
|
71
77
|
// Collect both project cards and template cards.
|
|
72
|
-
const cards = await this.collectCards(
|
|
78
|
+
const cards = await this.collectCards(
|
|
79
|
+
this.content.name,
|
|
80
|
+
(card, cardTypeName) => card.metadata?.cardType === cardTypeName,
|
|
81
|
+
);
|
|
73
82
|
|
|
74
83
|
if (op && op.name === 'change') {
|
|
75
84
|
const from = (op as ChangeOperation<string>).target;
|
|
@@ -102,38 +111,6 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
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
114
|
// When a field is removed, remove it from all affected cards.
|
|
138
115
|
private async handleRemoveField(cards: Card[], item: CustomField) {
|
|
139
116
|
for (const card of cards) {
|
|
@@ -151,7 +128,10 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
151
128
|
op: ChangeOperation<Type>,
|
|
152
129
|
) {
|
|
153
130
|
await this.verifyStateMapping(stateMapping, op);
|
|
154
|
-
const cards = await this.collectCards(
|
|
131
|
+
const cards = await this.collectCards(
|
|
132
|
+
this.content.name,
|
|
133
|
+
(card, cardTypeName) => card.metadata?.cardType === cardTypeName,
|
|
134
|
+
);
|
|
155
135
|
|
|
156
136
|
const unmappedStates: string[] = [];
|
|
157
137
|
|
|
@@ -189,15 +169,6 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
189
169
|
);
|
|
190
170
|
}
|
|
191
171
|
|
|
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
172
|
// Return link types that use this card type.
|
|
202
173
|
private relevantLinkTypes(): string[] {
|
|
203
174
|
const resourceName = resourceNameToString(this.resourceName);
|
|
@@ -233,8 +204,8 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
233
204
|
field = { name: target['name' as keyof Type] };
|
|
234
205
|
}
|
|
235
206
|
const fieldName = (field ? field.name : target) as string;
|
|
236
|
-
|
|
237
|
-
|
|
207
|
+
removeValue(content.alwaysVisibleFields, fieldName);
|
|
208
|
+
removeValue(content.optionallyVisibleFields, fieldName);
|
|
238
209
|
}
|
|
239
210
|
|
|
240
211
|
// Sets content container values to be either '[]' or with proper values.
|
|
@@ -246,21 +217,8 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
246
217
|
if (item.isCalculated === undefined) {
|
|
247
218
|
item.isCalculated = false;
|
|
248
219
|
}
|
|
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;
|
|
220
|
+
if (!item.name) {
|
|
221
|
+
continue;
|
|
264
222
|
}
|
|
265
223
|
}
|
|
266
224
|
} else {
|
|
@@ -400,10 +358,48 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
400
358
|
}
|
|
401
359
|
}
|
|
402
360
|
|
|
361
|
+
/**
|
|
362
|
+
* When resource name changes
|
|
363
|
+
* @param existingName Current resource name
|
|
364
|
+
*/
|
|
365
|
+
protected async onNameChange(existingName: string) {
|
|
366
|
+
const current = this.content;
|
|
367
|
+
const prefixes = this.project.projectPrefixes();
|
|
368
|
+
if (current.customFields) {
|
|
369
|
+
current.customFields.map(
|
|
370
|
+
(field) =>
|
|
371
|
+
(field.name = this.updatePrefixInResourceName(field.name, prefixes)),
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
if (current.alwaysVisibleFields) {
|
|
375
|
+
current.alwaysVisibleFields = current.alwaysVisibleFields.map((item) =>
|
|
376
|
+
this.updatePrefixInResourceName(item, prefixes),
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (current.optionallyVisibleFields) {
|
|
380
|
+
current.optionallyVisibleFields = current.optionallyVisibleFields.map(
|
|
381
|
+
(item) => this.updatePrefixInResourceName(item, prefixes),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
current.workflow = this.updatePrefixInResourceName(
|
|
385
|
+
current.workflow,
|
|
386
|
+
prefixes,
|
|
387
|
+
);
|
|
388
|
+
await Promise.all([
|
|
389
|
+
super.updateHandleBars(existingName, this.content.name),
|
|
390
|
+
super.updateCalculations(existingName, this.content.name),
|
|
391
|
+
super.updateCardContentReferences(existingName, this.content.name),
|
|
392
|
+
this.updateLinkTypes(existingName),
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
await this.write();
|
|
396
|
+
}
|
|
397
|
+
|
|
403
398
|
/**
|
|
404
399
|
* Creates a new card type object. Base class writes the object to disk automatically.
|
|
405
400
|
* @param workflowName Workflow name that this card type uses.
|
|
406
|
-
* @throws when workflow is empty, or
|
|
401
|
+
* @throws when workflow is empty, or
|
|
402
|
+
* when workflow does not exist in the project.
|
|
407
403
|
*/
|
|
408
404
|
public async createCardType(workflowName: string) {
|
|
409
405
|
if (!workflowName) {
|
|
@@ -438,7 +434,7 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
438
434
|
public async rename(newName: ResourceName) {
|
|
439
435
|
const existingName = this.content.name;
|
|
440
436
|
await super.rename(newName);
|
|
441
|
-
return this.
|
|
437
|
+
return this.onNameChange(existingName);
|
|
442
438
|
}
|
|
443
439
|
|
|
444
440
|
/**
|
|
@@ -451,60 +447,51 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
451
447
|
op: Operation<Type>,
|
|
452
448
|
) {
|
|
453
449
|
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
|
-
|
|
450
|
+
|
|
451
|
+
if (key === 'name' || key === 'description' || key === 'displayName') {
|
|
452
|
+
await super.update(updateKey, op);
|
|
453
|
+
} else {
|
|
454
|
+
const content = structuredClone(this.content);
|
|
455
|
+
const customFieldsChange = key === 'customFields';
|
|
456
|
+
if (key === 'alwaysVisibleFields') {
|
|
457
|
+
await this.validateFieldType(key, op);
|
|
458
|
+
content.alwaysVisibleFields = super.handleArray(
|
|
459
|
+
op,
|
|
460
|
+
key,
|
|
461
|
+
content.alwaysVisibleFields as Type[],
|
|
462
|
+
) as string[];
|
|
463
|
+
} else if (key === 'optionallyVisibleFields') {
|
|
464
|
+
await this.validateFieldType(key, op);
|
|
465
|
+
content.optionallyVisibleFields = super.handleArray(
|
|
466
|
+
op,
|
|
467
|
+
key,
|
|
468
|
+
content.optionallyVisibleFields as Type[],
|
|
469
|
+
) as string[];
|
|
470
|
+
} else if (key === 'workflow') {
|
|
471
|
+
const changeOp = op as ChangeOperation<string>;
|
|
472
|
+
const stateMapping = changeOp.mappingTable?.stateMapping || {};
|
|
473
|
+
content.workflow = super.handleScalar(op) as string;
|
|
474
|
+
if (Object.keys(stateMapping).length > 0) {
|
|
475
|
+
await this.handleWorkflowChange(stateMapping, changeOp);
|
|
476
|
+
}
|
|
477
|
+
} else if (key === 'customFields') {
|
|
478
|
+
await this.validateFieldType(key, op);
|
|
479
|
+
content.customFields = super.handleArray(
|
|
480
|
+
op,
|
|
481
|
+
key,
|
|
482
|
+
content.customFields as Type[],
|
|
483
|
+
) as CustomField[];
|
|
484
|
+
if (op.name === 'remove') {
|
|
485
|
+
this.removeValueFromOtherArrays(op, content);
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
throw new Error(`Unknown property '${key}' for CardType`);
|
|
482
489
|
}
|
|
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);
|
|
490
|
+
await super.postUpdate(content, updateKey, op);
|
|
491
|
+
|
|
492
|
+
if (customFieldsChange) {
|
|
493
|
+
return this.handleCustomFieldsChange(op as ChangeOperation<string>);
|
|
492
494
|
}
|
|
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
495
|
}
|
|
509
496
|
}
|
|
510
497
|
|