@izara_project/izara-core-generate-service-code 1.0.95 → 1.0.96
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/package.json +1 -1
- package/src/emit/attributeTree/emitAttributeTreeSchemas.js +29 -48
- package/src/emit/attributeTree/emitAttributeTreeTargetSchemas.js +153 -0
- package/src/emit/attributeTree/resolveTargetServicePath.js +111 -0
- package/src/emit/custom/customFlowSteps.js +33 -4
- package/src/emit/custom/templates/FlowEndpoint_Main.ejs +66 -4
- package/src/emit/custom/templates/FlowStep_Main.ejs +27 -0
- package/src/generate.js +57 -1
- package/test/attributeTreeSchemas.test.js +88 -6
- package/test/customFlowApplicationArtifacts.test.js +130 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izara_project/izara-core-generate-service-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.96",
|
|
4
4
|
"description": "Code for locally generating per service files",
|
|
5
5
|
"homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
|
|
6
6
|
"repository": {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { resolveTargetServicePath } from './resolveTargetServicePath.js';
|
|
2
|
+
import { emitAttributeTreeTargetSchemas } from './emitAttributeTreeTargetSchemas.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Emits dynamic ObjectSchemas and RelationshipSchemas for attributeTree add-ons
|
|
3
6
|
*/
|
|
@@ -68,12 +71,10 @@ export function attributeTreeObjectSchemaManagedPaths(objects = []) {
|
|
|
68
71
|
|
|
69
72
|
for (const { attributeTreeTag } of trees) {
|
|
70
73
|
const tag = attributeTreeTag;
|
|
71
|
-
const tagCap = _capitalize(tag);
|
|
72
74
|
paths.push(
|
|
73
75
|
`app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Link.js`,
|
|
74
|
-
`app/src/schemas/ObjectSchemas/generatedAttributeTree
|
|
75
|
-
`app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Label.js
|
|
76
|
-
`app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}ValueStandard.js`
|
|
76
|
+
`app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Value.js`,
|
|
77
|
+
`app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Label.js`
|
|
77
78
|
);
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -144,14 +145,14 @@ export function emitAttributeTreeObjectSchemas({ objects = [] } = {}) {
|
|
|
144
145
|
content: _renderObjectSchema(linkSchema)
|
|
145
146
|
});
|
|
146
147
|
|
|
147
|
-
// 2.
|
|
148
|
+
// 2. {tag}Value.js
|
|
148
149
|
const valueSchema = {
|
|
149
|
-
objectType:
|
|
150
|
+
objectType: `${tag}Value`,
|
|
150
151
|
canDelete: true,
|
|
151
152
|
generatedBy: 'user',
|
|
152
153
|
storageResources: graphStorageResources,
|
|
153
154
|
fieldNames: {
|
|
154
|
-
[
|
|
155
|
+
[`${tag}ValueId`]: {
|
|
155
156
|
type: 'string',
|
|
156
157
|
randomOnCreate: true,
|
|
157
158
|
canUpdate: false,
|
|
@@ -167,12 +168,12 @@ export function emitAttributeTreeObjectSchemas({ objects = [] } = {}) {
|
|
|
167
168
|
identifiers: [
|
|
168
169
|
{
|
|
169
170
|
type: 'identifier',
|
|
170
|
-
fieldName:
|
|
171
|
+
fieldName: `${tag}ValueId`
|
|
171
172
|
}
|
|
172
173
|
]
|
|
173
174
|
};
|
|
174
175
|
artifacts.push({
|
|
175
|
-
path: `app/src/schemas/ObjectSchemas/generatedAttributeTree
|
|
176
|
+
path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Value.js`,
|
|
176
177
|
content: _renderObjectSchema(valueSchema)
|
|
177
178
|
});
|
|
178
179
|
|
|
@@ -207,38 +208,6 @@ export function emitAttributeTreeObjectSchemas({ objects = [] } = {}) {
|
|
|
207
208
|
path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}Label.js`,
|
|
208
209
|
content: _renderObjectSchema(labelSchema)
|
|
209
210
|
});
|
|
210
|
-
|
|
211
|
-
// 4. {tag}ValueStandard.js
|
|
212
|
-
const standardValueSchema = {
|
|
213
|
-
objectType: `${tag}ValueStandard`,
|
|
214
|
-
canDelete: true,
|
|
215
|
-
generatedBy: 'user',
|
|
216
|
-
storageResources: graphStorageResources,
|
|
217
|
-
fieldNames: {
|
|
218
|
-
[`${tag}ValueStandardId`]: {
|
|
219
|
-
type: 'string',
|
|
220
|
-
randomOnCreate: true,
|
|
221
|
-
canUpdate: false,
|
|
222
|
-
storageResourceTags
|
|
223
|
-
},
|
|
224
|
-
value: {
|
|
225
|
-
type: 'string',
|
|
226
|
-
requiredOnCreate: true,
|
|
227
|
-
canUpdate: true,
|
|
228
|
-
storageResourceTags
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
identifiers: [
|
|
232
|
-
{
|
|
233
|
-
type: 'identifier',
|
|
234
|
-
fieldName: `${tag}ValueStandardId`
|
|
235
|
-
}
|
|
236
|
-
]
|
|
237
|
-
};
|
|
238
|
-
artifacts.push({
|
|
239
|
-
path: `app/src/schemas/ObjectSchemas/generatedAttributeTree/${tag}ValueStandard.js`,
|
|
240
|
-
content: _renderObjectSchema(standardValueSchema)
|
|
241
|
-
});
|
|
242
211
|
}
|
|
243
212
|
|
|
244
213
|
return artifacts;
|
|
@@ -268,6 +237,7 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
268
237
|
const hasLinkRel = {
|
|
269
238
|
fieldNames: {},
|
|
270
239
|
storageResources: graphStorageResources,
|
|
240
|
+
generatedBy: 'user',
|
|
271
241
|
links: [
|
|
272
242
|
{
|
|
273
243
|
storageResourceTags,
|
|
@@ -283,7 +253,8 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
283
253
|
serviceTag: effectiveServiceTag,
|
|
284
254
|
objectType: `${tag}Link`
|
|
285
255
|
},
|
|
286
|
-
linkType: 'many'
|
|
256
|
+
linkType: 'many',
|
|
257
|
+
requiredOnCreate: true
|
|
287
258
|
}
|
|
288
259
|
}
|
|
289
260
|
]
|
|
@@ -297,6 +268,7 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
297
268
|
const disabledLinkRel = {
|
|
298
269
|
fieldNames: {},
|
|
299
270
|
storageResources: graphStorageResources,
|
|
271
|
+
generatedBy: 'user',
|
|
300
272
|
links: [
|
|
301
273
|
{
|
|
302
274
|
storageResourceTags,
|
|
@@ -329,6 +301,7 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
329
301
|
const isRel = {
|
|
330
302
|
fieldNames: {},
|
|
331
303
|
storageResources: graphStorageResources,
|
|
304
|
+
generatedBy: 'systemNoUser',
|
|
332
305
|
links: [
|
|
333
306
|
{
|
|
334
307
|
storageResourceTags,
|
|
@@ -337,14 +310,15 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
337
310
|
serviceTag: effectiveServiceTag,
|
|
338
311
|
objectType: `${tag}Link`
|
|
339
312
|
},
|
|
340
|
-
linkType: '
|
|
313
|
+
linkType: 'many',
|
|
314
|
+
requiredOnCreate: true
|
|
341
315
|
},
|
|
342
316
|
to: {
|
|
343
317
|
objType: {
|
|
344
318
|
serviceTag: effectiveServiceTag,
|
|
345
|
-
objectType:
|
|
319
|
+
objectType: `${tag}Value`
|
|
346
320
|
},
|
|
347
|
-
linkType: '
|
|
321
|
+
linkType: 'one'
|
|
348
322
|
}
|
|
349
323
|
}
|
|
350
324
|
]
|
|
@@ -358,22 +332,24 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
358
332
|
const isLabelRel = {
|
|
359
333
|
fieldNames: {},
|
|
360
334
|
storageResources: graphStorageResources,
|
|
335
|
+
generatedBy: 'systemNoUser',
|
|
361
336
|
links: [
|
|
362
337
|
{
|
|
363
338
|
storageResourceTags,
|
|
364
339
|
from: {
|
|
365
340
|
objType: {
|
|
366
341
|
serviceTag: effectiveServiceTag,
|
|
367
|
-
objectType: `${tag}
|
|
342
|
+
objectType: `${tag}Value`
|
|
368
343
|
},
|
|
369
|
-
linkType: '
|
|
344
|
+
linkType: 'many',
|
|
345
|
+
requiredOnCreate: true
|
|
370
346
|
},
|
|
371
347
|
to: {
|
|
372
348
|
objType: {
|
|
373
349
|
serviceTag: effectiveServiceTag,
|
|
374
350
|
objectType: `${tag}Label`
|
|
375
351
|
},
|
|
376
|
-
linkType: '
|
|
352
|
+
linkType: 'one'
|
|
377
353
|
}
|
|
378
354
|
}
|
|
379
355
|
]
|
|
@@ -386,3 +362,8 @@ export function emitAttributeTreeRelationshipSchemas({
|
|
|
386
362
|
|
|
387
363
|
return artifacts;
|
|
388
364
|
}
|
|
365
|
+
|
|
366
|
+
export {
|
|
367
|
+
resolveTargetServicePath,
|
|
368
|
+
emitAttributeTreeTargetSchemas
|
|
369
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
function _renderObjectSchema(schema) {
|
|
4
|
+
return `export default [\n ${JSON.stringify(schema, null, 2).replace(/\n/g, '\n ')}\n];\n`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Emits standard schemas into target service folder for attributeTree.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} params
|
|
11
|
+
* @param {string} params.attributeTag - e.g. 'sellOfferTerm'
|
|
12
|
+
* @param {string} params.targetServiceRoot - Target service root directory
|
|
13
|
+
* @param {Object} [params.graphStorageResources] - Graph storage resources
|
|
14
|
+
* @returns {Array<{ path: string, content: string }>} Emitted target artifacts
|
|
15
|
+
*/
|
|
16
|
+
export function emitAttributeTreeTargetSchemas({
|
|
17
|
+
attributeTag,
|
|
18
|
+
targetServiceRoot,
|
|
19
|
+
graphStorageResources = {
|
|
20
|
+
myGraph: {
|
|
21
|
+
storageType: 'graph',
|
|
22
|
+
graphServerTag: 'GraphHandler'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} = {}) {
|
|
26
|
+
if (!attributeTag || !targetServiceRoot) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tag = attributeTag;
|
|
31
|
+
const storageResourceTags = Object.keys(graphStorageResources);
|
|
32
|
+
|
|
33
|
+
// 1. {tag}ValueStandardPropertyLabel.js
|
|
34
|
+
const labelSchema = {
|
|
35
|
+
objectType: `${tag}ValueStandardPropertyLabel`,
|
|
36
|
+
canDelete: true,
|
|
37
|
+
generatedBy: 'user',
|
|
38
|
+
storageResources: graphStorageResources,
|
|
39
|
+
fieldNames: {
|
|
40
|
+
[`${tag}ValueStandardPropertyLabelId`]: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
randomOnCreate: true,
|
|
43
|
+
canUpdate: false,
|
|
44
|
+
storageResourceTags
|
|
45
|
+
},
|
|
46
|
+
label: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
requiredOnCreate: true,
|
|
49
|
+
canUpdate: true,
|
|
50
|
+
storageResourceTags
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
identifiers: [
|
|
54
|
+
{
|
|
55
|
+
type: 'identifier',
|
|
56
|
+
fieldName: `${tag}ValueStandardPropertyLabelId`
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// 2. {tag}ValueStandardPropertyValue.js
|
|
62
|
+
const valueSchema = {
|
|
63
|
+
objectType: `${tag}ValueStandardPropertyValue`,
|
|
64
|
+
canDelete: true,
|
|
65
|
+
generatedBy: 'user',
|
|
66
|
+
storageResources: graphStorageResources,
|
|
67
|
+
fieldNames: {
|
|
68
|
+
[`${tag}ValueStandardPropertyValueId`]: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
randomOnCreate: true,
|
|
71
|
+
canUpdate: false,
|
|
72
|
+
storageResourceTags
|
|
73
|
+
},
|
|
74
|
+
value: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
requiredOnCreate: true,
|
|
77
|
+
canUpdate: true,
|
|
78
|
+
storageResourceTags
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
identifiers: [
|
|
82
|
+
{
|
|
83
|
+
type: 'identifier',
|
|
84
|
+
fieldName: `${tag}ValueStandardPropertyValueId`
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// 3. {tag}ValueStandard.js
|
|
90
|
+
const standardValueSchema = {
|
|
91
|
+
objectType: `${tag}ValueStandard`,
|
|
92
|
+
canDelete: true,
|
|
93
|
+
generatedBy: 'user',
|
|
94
|
+
storageResources: graphStorageResources,
|
|
95
|
+
fieldNames: {
|
|
96
|
+
[`${tag}ValueStandardId`]: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
randomOnCreate: true,
|
|
99
|
+
canUpdate: false,
|
|
100
|
+
storageResourceTags
|
|
101
|
+
},
|
|
102
|
+
value: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
requiredOnCreate: true,
|
|
105
|
+
canUpdate: true,
|
|
106
|
+
storageResourceTags
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
identifiers: [
|
|
110
|
+
{
|
|
111
|
+
type: 'identifier',
|
|
112
|
+
fieldName: `${tag}ValueStandardId`
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const relDir = path.join(
|
|
118
|
+
'app',
|
|
119
|
+
'src',
|
|
120
|
+
'schemas',
|
|
121
|
+
'ObjectSchemas',
|
|
122
|
+
'generatedCode',
|
|
123
|
+
'attributeTree',
|
|
124
|
+
'propertyValue'
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return [
|
|
128
|
+
{
|
|
129
|
+
path: path.join(
|
|
130
|
+
targetServiceRoot,
|
|
131
|
+
relDir,
|
|
132
|
+
`${tag}ValueStandardPropertyLabel.js`
|
|
133
|
+
),
|
|
134
|
+
content: _renderObjectSchema(labelSchema)
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
path: path.join(
|
|
138
|
+
targetServiceRoot,
|
|
139
|
+
relDir,
|
|
140
|
+
`${tag}ValueStandardPropertyValue.js`
|
|
141
|
+
),
|
|
142
|
+
content: _renderObjectSchema(valueSchema)
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
path: path.join(
|
|
146
|
+
targetServiceRoot,
|
|
147
|
+
relDir,
|
|
148
|
+
`${tag}ValueStandard.js`
|
|
149
|
+
),
|
|
150
|
+
content: _renderObjectSchema(standardValueSchema)
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves target service root path based on attributeTag and developer suffix.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} params
|
|
8
|
+
* @param {string} params.currentServiceRoot - Root path of the current service
|
|
9
|
+
* @param {string} params.attributeTag - The attribute tag, e.g. 'sellOfferTerm'
|
|
10
|
+
* @param {string} [params.customOverridePath] - Optional custom path override
|
|
11
|
+
* @returns {string|null} Resolved absolute path or null if not found
|
|
12
|
+
*/
|
|
13
|
+
export function resolveTargetServicePath({
|
|
14
|
+
currentServiceRoot,
|
|
15
|
+
attributeTag,
|
|
16
|
+
customOverridePath
|
|
17
|
+
} = {}) {
|
|
18
|
+
if (!currentServiceRoot || !attributeTag) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (customOverridePath) {
|
|
23
|
+
const resolvedCustom = path.resolve(
|
|
24
|
+
currentServiceRoot,
|
|
25
|
+
customOverridePath
|
|
26
|
+
);
|
|
27
|
+
if (fs.existsSync(resolvedCustom)) {
|
|
28
|
+
return resolvedCustom;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const currentFolderName = path.basename(currentServiceRoot);
|
|
33
|
+
const parentDir = path.resolve(currentServiceRoot, '..');
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(parentDir)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let entries = [];
|
|
40
|
+
try {
|
|
41
|
+
entries = fs.readdirSync(parentDir, { withFileTypes: true });
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const suffixMatch = currentFolderName.match(/-([a-zA-Z0-9]+)$/);
|
|
47
|
+
let devSuffix = '';
|
|
48
|
+
if (suffixMatch && suffixMatch[1]) {
|
|
49
|
+
devSuffix = suffixMatch[1].toLowerCase();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const normalizedTag = attributeTag.toLowerCase();
|
|
53
|
+
const kebabTag = attributeTag
|
|
54
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
55
|
+
.toLowerCase();
|
|
56
|
+
|
|
57
|
+
const priority1Matches = [];
|
|
58
|
+
const priority2Matches = [];
|
|
59
|
+
const priority3Matches = [];
|
|
60
|
+
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
if (!entry.isDirectory()) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (entry.name === currentFolderName) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const folderNameLower = entry.name.toLowerCase();
|
|
71
|
+
const hasTagMatch =
|
|
72
|
+
folderNameLower.includes(normalizedTag) ||
|
|
73
|
+
folderNameLower.includes(kebabTag);
|
|
74
|
+
|
|
75
|
+
if (!hasTagMatch) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fullPath = path.join(parentDir, entry.name);
|
|
80
|
+
const hasStructure =
|
|
81
|
+
fs.existsSync(path.join(fullPath, 'app', 'src')) ||
|
|
82
|
+
fs.existsSync(path.join(fullPath, 'package.json'));
|
|
83
|
+
|
|
84
|
+
if (!hasStructure) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (devSuffix !== '' && folderNameLower.endsWith('-' + devSuffix)) {
|
|
89
|
+
priority1Matches.push(fullPath);
|
|
90
|
+
} else if (
|
|
91
|
+
folderNameLower.endsWith(normalizedTag) ||
|
|
92
|
+
folderNameLower.endsWith(kebabTag)
|
|
93
|
+
) {
|
|
94
|
+
priority2Matches.push(fullPath);
|
|
95
|
+
} else {
|
|
96
|
+
priority3Matches.push(fullPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (priority1Matches.length > 0) {
|
|
101
|
+
return priority1Matches[0];
|
|
102
|
+
}
|
|
103
|
+
if (priority2Matches.length > 0) {
|
|
104
|
+
return priority2Matches[0];
|
|
105
|
+
}
|
|
106
|
+
if (priority3Matches.length > 0) {
|
|
107
|
+
return priority3Matches[0];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
@@ -53,10 +53,19 @@ export async function emitExtTopicStep({ flow, step, flowTag, stepName }) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const flowSteps = flow?.flowSteps ?? [];
|
|
57
|
+
const isLastStep =
|
|
58
|
+
flowSteps.length > 1 &&
|
|
59
|
+
flowSteps[flowSteps.length - 1]?.stepName === step?.stepName;
|
|
60
|
+
|
|
56
61
|
const artifacts = [
|
|
57
62
|
{
|
|
58
63
|
path: `${source}/${mainFileName}.js`,
|
|
59
|
-
content: await renderStepMain(functionName, _startConfig(flow)
|
|
64
|
+
content: await renderStepMain(functionName, _startConfig(flow), {
|
|
65
|
+
flow,
|
|
66
|
+
flowTag: flow.flowTag,
|
|
67
|
+
isLastStep
|
|
68
|
+
})
|
|
60
69
|
}
|
|
61
70
|
];
|
|
62
71
|
const functions = [];
|
|
@@ -107,10 +116,19 @@ export async function emitDsqStep({ flow, step, flowTag, stepName }) {
|
|
|
107
116
|
stepEvents = step.event;
|
|
108
117
|
}
|
|
109
118
|
const artifacts = [];
|
|
119
|
+
const flowSteps = flow?.flowSteps ?? [];
|
|
120
|
+
const isLastStep =
|
|
121
|
+
flowSteps.length > 1 &&
|
|
122
|
+
flowSteps[flowSteps.length - 1]?.stepName === step?.stepName;
|
|
123
|
+
|
|
110
124
|
if (!stepEvents.includes('extTopic')) {
|
|
111
125
|
artifacts.push({
|
|
112
126
|
path: `${source}/${mainFileName}.js`,
|
|
113
|
-
content: await renderStepMain(functionName, startConfig
|
|
127
|
+
content: await renderStepMain(functionName, startConfig, {
|
|
128
|
+
flow,
|
|
129
|
+
flowTag: flow.flowTag,
|
|
130
|
+
isLastStep
|
|
131
|
+
})
|
|
114
132
|
});
|
|
115
133
|
}
|
|
116
134
|
const functions = [];
|
|
@@ -343,8 +361,19 @@ export function storedCacheTableName(flow, objects) {
|
|
|
343
361
|
);
|
|
344
362
|
}
|
|
345
363
|
|
|
346
|
-
export async function renderStepMain(
|
|
347
|
-
|
|
364
|
+
export async function renderStepMain(
|
|
365
|
+
functionName,
|
|
366
|
+
startConfig = null,
|
|
367
|
+
options = {}
|
|
368
|
+
) {
|
|
369
|
+
const { flow, flowTag, isLastStep } = options;
|
|
370
|
+
return renderCustom('FlowStep_Main.ejs', {
|
|
371
|
+
functionName,
|
|
372
|
+
startConfig,
|
|
373
|
+
flow,
|
|
374
|
+
flowTag: flowTag || flow?.flowTag,
|
|
375
|
+
isLastStep: Boolean(isLastStep)
|
|
376
|
+
});
|
|
348
377
|
}
|
|
349
378
|
|
|
350
379
|
export function renderMain(functionName) {
|
|
@@ -26,6 +26,9 @@ import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
|
26
26
|
<% if (typeof flow !== 'undefined' && flow.statusType === 'storedCache') { %>
|
|
27
27
|
import storedCacheSharedLib from '@izara_project/izara-core-library-stored-cache';
|
|
28
28
|
<% } %>
|
|
29
|
+
<% if (typeof flow !== 'undefined' && flow.statusType === 'statusField') { %>
|
|
30
|
+
import statusFieldSharedLib from '@izara_project/izara-core-library-status-field';
|
|
31
|
+
<% } %>
|
|
29
32
|
<% if (typeof flow !== 'undefined' && flow.statusType === 'triggerCache') { %>
|
|
30
33
|
import { triggeredCacheSharedLibV2 } from '@izara_project/izara-core-library-trigger-cache';
|
|
31
34
|
<% } %>
|
|
@@ -69,13 +72,48 @@ export default async function <%- typeof functionName !== 'undefined' ? function
|
|
|
69
72
|
//(</additionalParams>)
|
|
70
73
|
) {
|
|
71
74
|
try {
|
|
72
|
-
_izContext.logger.debug(
|
|
73
|
-
|
|
74
|
-
_izContext.logger.debug(
|
|
75
|
-
`[${process.env.iz_serviceTag}:<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>] callingFlowConfig:`,
|
|
75
|
+
_izContext.logger.debug('[<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>] started', {
|
|
76
|
+
requestParams,
|
|
76
77
|
callingFlowConfig
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
<% if (typeof flow !== 'undefined' && flow.statusType === 'statusField') { -%>
|
|
81
|
+
|
|
82
|
+
//(<beforeCheckedStatusField>)
|
|
83
|
+
//(</beforeCheckedStatusField>)
|
|
84
|
+
|
|
85
|
+
let [status, flowStatusFieldRecord] = await statusFieldSharedLib.checkStatusField(
|
|
86
|
+
_izContext,
|
|
87
|
+
{
|
|
88
|
+
flowTag: '<%= flowTag %>',
|
|
89
|
+
serviceTag: process.env.iz_serviceTag
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
//(<identifiers>)
|
|
93
|
+
//(</identifiers>)
|
|
94
|
+
},
|
|
95
|
+
//(<overwriteUniqueRequestId>)
|
|
96
|
+
//(</overwriteUniqueRequestId>)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
_izContext.logger.debug(
|
|
100
|
+
`[${process.env.iz_serviceTag}:<%- typeof functionName !== 'undefined' ? functionName : upperCaseFlowTag %>] checkStatusField result:`,
|
|
101
|
+
{ status, flowStatusFieldRecord }
|
|
77
102
|
);
|
|
78
103
|
|
|
104
|
+
//(<afterCheckStatusField>)
|
|
105
|
+
//(</afterCheckStatusField>)
|
|
106
|
+
|
|
107
|
+
if (status !== 'process') {
|
|
108
|
+
if (status === 'error' || status === 'complete') {
|
|
109
|
+
//(<afterValidateStatusField>)
|
|
110
|
+
//(</afterValidateStatusField>)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//(<checkStatusFieldHook>)
|
|
114
|
+
//(</checkStatusFieldHook>)
|
|
115
|
+
}
|
|
116
|
+
<% } -%>
|
|
79
117
|
<% if (typeof flow !== 'undefined' && flow.statusType === 'triggerCache') { -%>
|
|
80
118
|
|
|
81
119
|
//(<beforeCheckTriggeredCache>)
|
|
@@ -217,6 +255,30 @@ export default async function <%- typeof functionName !== 'undefined' ? function
|
|
|
217
255
|
|
|
218
256
|
//(<endpointHook>)
|
|
219
257
|
//(</endpointHook>)
|
|
258
|
+
<% if (typeof flow !== 'undefined' && flow.statusType === 'statusField' && (!flow.flowSteps || flow.flowSteps.length <= 1)) { -%>
|
|
259
|
+
|
|
260
|
+
//(<beforeCompleteStatusField>)
|
|
261
|
+
//(</beforeCompleteStatusField>)
|
|
262
|
+
|
|
263
|
+
await statusFieldSharedLib.completeStatusField(
|
|
264
|
+
_izContext,
|
|
265
|
+
{
|
|
266
|
+
flowTag: '<%= flowTag %>',
|
|
267
|
+
serviceTag: process.env.iz_serviceTag
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
//(<identifiers>)
|
|
271
|
+
//(</identifiers>)
|
|
272
|
+
},
|
|
273
|
+
//(<statusField>)
|
|
274
|
+
//(</statusField>)
|
|
275
|
+
//(<errorFounds>)
|
|
276
|
+
//(</errorFounds>)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
//(<afterCompleteStatusField>)
|
|
280
|
+
//(</afterCompleteStatusField>)
|
|
281
|
+
<% } -%>
|
|
220
282
|
<% } -%>
|
|
221
283
|
|
|
222
284
|
} catch (err) {
|
|
@@ -16,6 +16,9 @@ along with this program. If not, see
|
|
|
16
16
|
<http: //www.gnu.org/licenses />.
|
|
17
17
|
*/
|
|
18
18
|
import { identifierUuid } from '@izara_project/izara-shared-core';
|
|
19
|
+
<% if (typeof flow !== 'undefined' && flow.statusType === 'statusField' && typeof isLastStep !== 'undefined' && isLastStep) { %>
|
|
20
|
+
import statusFieldSharedLib from '@izara_project/izara-core-library-status-field';
|
|
21
|
+
<% } %>
|
|
19
22
|
|
|
20
23
|
//(<optionalRequire>)
|
|
21
24
|
//(</optionalRequire>)
|
|
@@ -51,6 +54,30 @@ export default async function <%= functionName %>(
|
|
|
51
54
|
|
|
52
55
|
//(<hookCode>)
|
|
53
56
|
//(</hookCode>)
|
|
57
|
+
<% if (typeof flow !== 'undefined' && flow.statusType === 'statusField' && typeof isLastStep !== 'undefined' && isLastStep) { -%>
|
|
58
|
+
|
|
59
|
+
//(<beforeCompleteStatusField>)
|
|
60
|
+
//(</beforeCompleteStatusField>)
|
|
61
|
+
|
|
62
|
+
await statusFieldSharedLib.completeStatusField(
|
|
63
|
+
_izContext,
|
|
64
|
+
{
|
|
65
|
+
flowTag: '<%= flowTag %>',
|
|
66
|
+
serviceTag: process.env.iz_serviceTag
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
//(<identifiers>)
|
|
70
|
+
//(</identifiers>)
|
|
71
|
+
},
|
|
72
|
+
//(<statusField>)
|
|
73
|
+
//(</statusField>)
|
|
74
|
+
//(<errorFounds>)
|
|
75
|
+
//(</errorFounds>)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
//(<afterCompleteStatusField>)
|
|
79
|
+
//(</afterCompleteStatusField>)
|
|
80
|
+
<% } -%>
|
|
54
81
|
} catch (err) {
|
|
55
82
|
_izContext.logger.error(`[${process.env.iz_serviceTag}:<%= functionName %>] error:`, err);
|
|
56
83
|
throw err;
|
package/src/generate.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { buildGenerationPlan } from './plan/buildGenerationPlan.js';
|
|
2
4
|
import { commitArtifacts } from './output/commitArtifacts.js';
|
|
3
5
|
import { compileProject } from './compile/compileProject.js';
|
|
@@ -59,7 +61,9 @@ import {
|
|
|
59
61
|
emitAttributeTreeObjectSchemas,
|
|
60
62
|
emitAttributeTreeRelationshipSchemas,
|
|
61
63
|
attributeTreeObjectSchemaManagedPaths,
|
|
62
|
-
attributeTreeRelationshipSchemaManagedPaths
|
|
64
|
+
attributeTreeRelationshipSchemaManagedPaths,
|
|
65
|
+
resolveTargetServicePath,
|
|
66
|
+
emitAttributeTreeTargetSchemas
|
|
63
67
|
} from './emit/attributeTree/emitAttributeTreeSchemas.js';
|
|
64
68
|
import {
|
|
65
69
|
emitErrorSchemas,
|
|
@@ -147,6 +151,11 @@ export async function generate(rootPath, options = {}) {
|
|
|
147
151
|
)
|
|
148
152
|
);
|
|
149
153
|
}
|
|
154
|
+
await _emitTargetServiceAttributeTreeSchemas({
|
|
155
|
+
project,
|
|
156
|
+
objects,
|
|
157
|
+
logger: options.logger
|
|
158
|
+
});
|
|
150
159
|
const durationMs = Date.now() - startedAt;
|
|
151
160
|
console.log(
|
|
152
161
|
`[INFO] [Generate] STATUS=FINISHED | ` +
|
|
@@ -343,3 +352,50 @@ async function _emitGroups({
|
|
|
343
352
|
}
|
|
344
353
|
];
|
|
345
354
|
}
|
|
355
|
+
|
|
356
|
+
async function _emitTargetServiceAttributeTreeSchemas({
|
|
357
|
+
project,
|
|
358
|
+
objects,
|
|
359
|
+
logger
|
|
360
|
+
}) {
|
|
361
|
+
if (!Array.isArray(objects)) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
for (const object of objects) {
|
|
365
|
+
if (!Array.isArray(object?.addOnDataStructure)) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
for (const addOn of object.addOnDataStructure) {
|
|
369
|
+
if (addOn?.type === 'attributeTree' && addOn?.attributeTreeTag) {
|
|
370
|
+
const attributeTag = addOn.attributeTreeTag;
|
|
371
|
+
const targetServiceRoot = resolveTargetServicePath({
|
|
372
|
+
currentServiceRoot: project.rootPath,
|
|
373
|
+
attributeTag
|
|
374
|
+
});
|
|
375
|
+
if (targetServiceRoot) {
|
|
376
|
+
const artifacts = emitAttributeTreeTargetSchemas({
|
|
377
|
+
attributeTag,
|
|
378
|
+
targetServiceRoot
|
|
379
|
+
});
|
|
380
|
+
for (const artifact of artifacts) {
|
|
381
|
+
await fs.promises.mkdir(path.dirname(artifact.path), {
|
|
382
|
+
recursive: true
|
|
383
|
+
});
|
|
384
|
+
await fs.promises.writeFile(
|
|
385
|
+
artifact.path,
|
|
386
|
+
artifact.content,
|
|
387
|
+
'utf8'
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
if (logger && typeof logger.info === 'function') {
|
|
391
|
+
logger.info(
|
|
392
|
+
`[INFO] [AttributeTree] Target schemas emitted for ` +
|
|
393
|
+
`tag=${attributeTag} to ${targetServiceRoot}`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
emitAttributeTreeObjectSchemas,
|
|
9
9
|
emitAttributeTreeRelationshipSchemas,
|
|
10
10
|
attributeTreeObjectSchemaManagedPaths,
|
|
11
|
-
attributeTreeRelationshipSchemaManagedPaths
|
|
11
|
+
attributeTreeRelationshipSchemaManagedPaths,
|
|
12
|
+
resolveTargetServicePath,
|
|
13
|
+
emitAttributeTreeTargetSchemas
|
|
12
14
|
} from '../src/emit/attributeTree/emitAttributeTreeSchemas.js';
|
|
13
15
|
import { generate } from '../src/generate.js';
|
|
14
16
|
|
|
@@ -22,7 +24,7 @@ after(async () => {
|
|
|
22
24
|
);
|
|
23
25
|
});
|
|
24
26
|
|
|
25
|
-
test('emitAttributeTreeObjectSchemas returns
|
|
27
|
+
test('emitAttributeTreeObjectSchemas returns 3 object schemas per attributeTree tag', () => {
|
|
26
28
|
const objects = [
|
|
27
29
|
{
|
|
28
30
|
objectType: 'sellOffer',
|
|
@@ -47,16 +49,21 @@ test('emitAttributeTreeObjectSchemas returns 4 object schemas per attributeTree
|
|
|
47
49
|
];
|
|
48
50
|
|
|
49
51
|
const artifacts = emitAttributeTreeObjectSchemas({ objects });
|
|
50
|
-
assert.equal(artifacts.length,
|
|
52
|
+
assert.equal(artifacts.length, 3);
|
|
51
53
|
|
|
52
54
|
const paths = artifacts.map((a) => a.path);
|
|
53
55
|
assert.deepEqual(paths, [
|
|
54
56
|
'app/src/schemas/ObjectSchemas/generatedAttributeTree/sellOfferTermLink.js',
|
|
55
|
-
'app/src/schemas/ObjectSchemas/generatedAttributeTree/
|
|
56
|
-
'app/src/schemas/ObjectSchemas/generatedAttributeTree/sellOfferTermLabel.js'
|
|
57
|
-
'app/src/schemas/ObjectSchemas/generatedAttributeTree/sellOfferTermValueStandard.js'
|
|
57
|
+
'app/src/schemas/ObjectSchemas/generatedAttributeTree/sellOfferTermValue.js',
|
|
58
|
+
'app/src/schemas/ObjectSchemas/generatedAttributeTree/sellOfferTermLabel.js'
|
|
58
59
|
]);
|
|
59
60
|
|
|
61
|
+
const valueArtifact = artifacts.find((a) =>
|
|
62
|
+
a.path.endsWith('sellOfferTermValue.js')
|
|
63
|
+
);
|
|
64
|
+
assert.match(valueArtifact.content, /"objectType": "sellOfferTermValue"/);
|
|
65
|
+
assert.match(valueArtifact.content, /"sellOfferTermValueId"/);
|
|
66
|
+
|
|
60
67
|
for (const artifact of artifacts) {
|
|
61
68
|
assert.doesNotMatch(artifact.content, /dynamoDB/);
|
|
62
69
|
assert.match(artifact.content, /GraphHandler/);
|
|
@@ -101,6 +108,31 @@ test('emitAttributeTreeRelationshipSchemas returns 4 relationship schemas per at
|
|
|
101
108
|
'app/src/schemas/RelationshipSchemas/generatedAttributeTree/isSellOfferTermLabel.js'
|
|
102
109
|
]);
|
|
103
110
|
|
|
111
|
+
const hasRel = artifacts.find((a) => a.path.endsWith('hasSellOfferTermLink.js'));
|
|
112
|
+
assert.match(hasRel.content, /"generatedBy": "user"/);
|
|
113
|
+
assert.match(hasRel.content, /"requiredOnCreate": true/);
|
|
114
|
+
|
|
115
|
+
const disabledRel = artifacts.find((a) =>
|
|
116
|
+
a.path.endsWith('disabledSellOfferTermLink.js')
|
|
117
|
+
);
|
|
118
|
+
assert.match(disabledRel.content, /"generatedBy": "user"/);
|
|
119
|
+
|
|
120
|
+
const isRel = artifacts.find((a) => a.path.endsWith('isSellOfferTerm.js'));
|
|
121
|
+
assert.match(isRel.content, /"generatedBy": "systemNoUser"/);
|
|
122
|
+
assert.match(isRel.content, /"objectType": "sellOfferTermValue"/);
|
|
123
|
+
assert.match(isRel.content, /"requiredOnCreate": true/);
|
|
124
|
+
|
|
125
|
+
const isLabelRel = artifacts.find((a) =>
|
|
126
|
+
a.path.endsWith('isSellOfferTermLabel.js')
|
|
127
|
+
);
|
|
128
|
+
assert.match(isLabelRel.content, /"generatedBy": "systemNoUser"/);
|
|
129
|
+
assert.match(isLabelRel.content, /"requiredOnCreate": true/);
|
|
130
|
+
// from objType should be sellOfferTermValue
|
|
131
|
+
assert.match(
|
|
132
|
+
isLabelRel.content,
|
|
133
|
+
/"from":\s*\{\s*"objType":\s*\{[^}]*"objectType": "sellOfferTermValue"/
|
|
134
|
+
);
|
|
135
|
+
|
|
104
136
|
for (const artifact of artifacts) {
|
|
105
137
|
assert.doesNotMatch(artifact.content, /dynamoDB/);
|
|
106
138
|
assert.match(artifact.content, /GraphHandler/);
|
|
@@ -217,3 +249,53 @@ async function createProjectWithAttributeTree({
|
|
|
217
249
|
|
|
218
250
|
return rootPath;
|
|
219
251
|
}
|
|
252
|
+
|
|
253
|
+
test('resolveTargetServicePath prioritizes dev suffix and matches attributeTag', async () => {
|
|
254
|
+
const tmpRoot = await mkdtemp(join(tmpdir(), 'izara-services-test-'));
|
|
255
|
+
projectRoots.add(tmpRoot);
|
|
256
|
+
|
|
257
|
+
const currentServiceDir = join(
|
|
258
|
+
tmpRoot,
|
|
259
|
+
'izara-market-products-sell-offer-manager-pong'
|
|
260
|
+
);
|
|
261
|
+
const targetWithSuffix = join(
|
|
262
|
+
tmpRoot,
|
|
263
|
+
'izara-market-products-sell-offer-term-pong'
|
|
264
|
+
);
|
|
265
|
+
const targetOtherDev = join(
|
|
266
|
+
tmpRoot,
|
|
267
|
+
'izara-market-products-sell-offer-term-alice'
|
|
268
|
+
);
|
|
269
|
+
const unrelatedService = join(
|
|
270
|
+
tmpRoot,
|
|
271
|
+
'izara-market-products-cart-pong'
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
await mkdir(join(currentServiceDir, 'app/src'), { recursive: true });
|
|
275
|
+
await mkdir(join(targetWithSuffix, 'app/src'), { recursive: true });
|
|
276
|
+
await mkdir(join(targetOtherDev, 'app/src'), { recursive: true });
|
|
277
|
+
await mkdir(join(unrelatedService, 'app/src'), { recursive: true });
|
|
278
|
+
|
|
279
|
+
const resolved = resolveTargetServicePath({
|
|
280
|
+
currentServiceRoot: currentServiceDir,
|
|
281
|
+
attributeTag: 'sellOfferTerm'
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
assert.equal(resolved, targetWithSuffix);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('emitAttributeTreeTargetSchemas produces expected standard schemas', () => {
|
|
288
|
+
const targetDir = '/dummy/path/izara-market-products-sellOfferTerm';
|
|
289
|
+
const artifacts = emitAttributeTreeTargetSchemas({
|
|
290
|
+
attributeTag: 'sellOfferTerm',
|
|
291
|
+
targetServiceRoot: targetDir
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
assert.equal(artifacts.length, 3);
|
|
295
|
+
const paths = artifacts.map((a) => a.path);
|
|
296
|
+
|
|
297
|
+
assert.ok(paths.some((p) => p.endsWith('sellOfferTermValueStandardPropertyLabel.js')));
|
|
298
|
+
assert.ok(paths.some((p) => p.endsWith('sellOfferTermValueStandardPropertyValue.js')));
|
|
299
|
+
assert.ok(paths.some((p) => p.endsWith('sellOfferTermValueStandard.js')));
|
|
300
|
+
});
|
|
301
|
+
|
|
@@ -1421,6 +1421,50 @@ test('emits early return cacheMain on cache hit in FlowEndpoint_Main for storedC
|
|
|
1421
1421
|
await parseAsEsm(mainPath);
|
|
1422
1422
|
});
|
|
1423
1423
|
|
|
1424
|
+
test('emits canonical checkStatusField and hooks in FlowEndpoint_Main for statusField', async () => {
|
|
1425
|
+
const rootPath = await createProject();
|
|
1426
|
+
await writeFile(
|
|
1427
|
+
join(rootPath, 'app/src/schemas/FlowSchemas/StatusFieldFlow.js'),
|
|
1428
|
+
`export default {
|
|
1429
|
+
flowTag: 'statusFieldOrder',
|
|
1430
|
+
event: ['ownTopic'],
|
|
1431
|
+
statusType: 'statusField',
|
|
1432
|
+
objType: { objectType: 'order', serviceTag: 'Current' },
|
|
1433
|
+
flowSteps: [{ stepName: 'Start' }]
|
|
1434
|
+
};\n`
|
|
1435
|
+
);
|
|
1436
|
+
|
|
1437
|
+
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
1438
|
+
|
|
1439
|
+
const mainPath = join(
|
|
1440
|
+
rootPath,
|
|
1441
|
+
'app/src/generatedCode/FlowSchemas/StatusFieldOrder/source/StatusFieldOrder_Main.js'
|
|
1442
|
+
);
|
|
1443
|
+
const mainContent = await readFile(mainPath, 'utf8');
|
|
1444
|
+
assert.match(
|
|
1445
|
+
mainContent,
|
|
1446
|
+
/import statusFieldSharedLib from '@izara_project\/izara-core-library-status-field';/
|
|
1447
|
+
);
|
|
1448
|
+
assert.match(mainContent, /statusFieldSharedLib\.checkStatusField/);
|
|
1449
|
+
assert.match(mainContent, /\/\/\(<beforeCheckedStatusField>\)/);
|
|
1450
|
+
assert.match(mainContent, /\/\/\(<identifiers>\)/);
|
|
1451
|
+
assert.match(mainContent, /\/\/\(<overwriteUniqueRequestId>\)/);
|
|
1452
|
+
assert.match(mainContent, /\/\/\(<afterCheckStatusField>\)/);
|
|
1453
|
+
assert.match(mainContent, /\/\/\(<afterValidateStatusField>\)/);
|
|
1454
|
+
assert.match(mainContent, /\/\/\(<checkStatusFieldHook>\)/);
|
|
1455
|
+
assert.match(mainContent, /\/\/\(<endpointHook>\)/);
|
|
1456
|
+
assert.match(mainContent, /\/\/\(<beforeCompleteStatusField>\)/);
|
|
1457
|
+
assert.match(mainContent, /statusFieldSharedLib\.completeStatusField/);
|
|
1458
|
+
assert.match(mainContent, /\/\/\(<statusField>\)/);
|
|
1459
|
+
assert.match(mainContent, /\/\/\(<errorFounds>\)/);
|
|
1460
|
+
assert.match(mainContent, /\/\/\(<afterCompleteStatusField>\)/);
|
|
1461
|
+
assert.match(
|
|
1462
|
+
mainContent,
|
|
1463
|
+
/_izContext\.logger\.debug\('\[StatusFieldOrder_Main\] started', \{\s*requestParams,\s*callingFlowConfig\s*\}\);/
|
|
1464
|
+
);
|
|
1465
|
+
await parseAsEsm(mainPath);
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1424
1468
|
test('does not emit RbacCallback artifacts or duplicate artifact paths for custom flow with RBAC', async () => {
|
|
1425
1469
|
const { generate } = await import('../src/generate.js');
|
|
1426
1470
|
const rootPath = await createProject();
|
|
@@ -1538,4 +1582,90 @@ test('skips checkRbacPermission in FlowEndpoint_Main when initiateBy is systemNo
|
|
|
1538
1582
|
await parseAsEsm(mainPath);
|
|
1539
1583
|
});
|
|
1540
1584
|
|
|
1585
|
+
test('emits completeStatusField only in the final flowStep for statusType statusField', async () => {
|
|
1586
|
+
const rootPath = await createProject();
|
|
1587
|
+
await writeFile(
|
|
1588
|
+
join(rootPath, 'app/src/schemas/FlowSchemas/MultiStepStatus.js'),
|
|
1589
|
+
`export default {
|
|
1590
|
+
flowTag: 'multiStepStatus',
|
|
1591
|
+
event: ['ownTopic'],
|
|
1592
|
+
statusType: 'statusField',
|
|
1593
|
+
objType: { objectType: 'order', serviceTag: 'Current' },
|
|
1594
|
+
flowSteps: [
|
|
1595
|
+
{
|
|
1596
|
+
stepName: 'Start',
|
|
1597
|
+
nextSteps: [
|
|
1598
|
+
{
|
|
1599
|
+
targetType: 'extTopic',
|
|
1600
|
+
serviceTag: 'PaymentService',
|
|
1601
|
+
topicName: 'Process_In',
|
|
1602
|
+
toStepNames: ['Process']
|
|
1603
|
+
}
|
|
1604
|
+
]
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
stepName: 'Process',
|
|
1608
|
+
event: ['extTopic'],
|
|
1609
|
+
previousSteps: [{ sourceType: 'ownTopic', stepName: 'Start' }],
|
|
1610
|
+
nextSteps: [
|
|
1611
|
+
{
|
|
1612
|
+
targetType: 'extTopic',
|
|
1613
|
+
serviceTag: 'PaymentService',
|
|
1614
|
+
topicName: 'Finalize_In',
|
|
1615
|
+
toStepNames: ['Finalize']
|
|
1616
|
+
}
|
|
1617
|
+
]
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
stepName: 'Finalize',
|
|
1621
|
+
event: ['extTopic'],
|
|
1622
|
+
previousSteps: [{ sourceType: 'extTopic', serviceTag: 'PaymentService', topicName: 'Process_Out', fromStepNames: ['Process'] }]
|
|
1623
|
+
}
|
|
1624
|
+
]
|
|
1625
|
+
};\n`
|
|
1626
|
+
);
|
|
1627
|
+
|
|
1628
|
+
await generateCustomFlowApplicationArtifacts(rootPath);
|
|
1629
|
+
|
|
1630
|
+
// 1. Start step (FlowEndpoint_Main) has checkStatusField, NOT completeStatusField
|
|
1631
|
+
const startPath = join(
|
|
1632
|
+
rootPath,
|
|
1633
|
+
'app/src/generatedCode/FlowSchemas/MultiStepStatus/source/MultiStepStatus_Main.js'
|
|
1634
|
+
);
|
|
1635
|
+
const startContent = await readFile(startPath, 'utf8');
|
|
1636
|
+
assert.match(startContent, /statusFieldSharedLib\.checkStatusField/);
|
|
1637
|
+
assert.doesNotMatch(startContent, /completeStatusField/);
|
|
1638
|
+
await parseAsEsm(startPath);
|
|
1639
|
+
|
|
1640
|
+
// 2. Middle step (Process) is not the last step, so NO completeStatusField
|
|
1641
|
+
const processPath = join(
|
|
1642
|
+
rootPath,
|
|
1643
|
+
'app/src/generatedCode/FlowSchemas/MultiStepStatus/source/MultiStepStatusProcess_Main.js'
|
|
1644
|
+
);
|
|
1645
|
+
const processContent = await readFile(processPath, 'utf8');
|
|
1646
|
+
assert.doesNotMatch(processContent, /statusFieldSharedLib/);
|
|
1647
|
+
assert.doesNotMatch(processContent, /completeStatusField/);
|
|
1648
|
+
await parseAsEsm(processPath);
|
|
1649
|
+
|
|
1650
|
+
// 3. Final step (Finalize) IS the last step, so it has completeStatusField and canonical hooks
|
|
1651
|
+
const finalizePath = join(
|
|
1652
|
+
rootPath,
|
|
1653
|
+
'app/src/generatedCode/FlowSchemas/MultiStepStatus/source/MultiStepStatusFinalize_Main.js'
|
|
1654
|
+
);
|
|
1655
|
+
const finalizeContent = await readFile(finalizePath, 'utf8');
|
|
1656
|
+
assert.match(
|
|
1657
|
+
finalizeContent,
|
|
1658
|
+
/import statusFieldSharedLib from '@izara_project\/izara-core-library-status-field';/
|
|
1659
|
+
);
|
|
1660
|
+
assert.match(finalizeContent, /\/\/\(<beforeCompleteStatusField>\)/);
|
|
1661
|
+
assert.match(finalizeContent, /statusFieldSharedLib\.completeStatusField/);
|
|
1662
|
+
assert.match(finalizeContent, /flowTag:\s*'multiStepStatus'/);
|
|
1663
|
+
assert.match(finalizeContent, /\/\/\(<identifiers>\)/);
|
|
1664
|
+
assert.match(finalizeContent, /\/\/\(<statusField>\)/);
|
|
1665
|
+
assert.match(finalizeContent, /\/\/\(<errorFounds>\)/);
|
|
1666
|
+
assert.match(finalizeContent, /\/\/\(<afterCompleteStatusField>\)/);
|
|
1667
|
+
await parseAsEsm(finalizePath);
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
|
|
1541
1671
|
|