@izara_project/izara-core-generate-service-code 1.0.98 → 1.0.100
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/compile/compileProject.js +16 -5
- package/src/compile/flowValidator.js +47 -8
- package/src/compile/loadSchemas.js +18 -7
- package/src/compile/schemaValidatorSetup.js +16 -0
- package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +123 -109
- package/src/emit/application/coreFunction/templates/InitialSetupConfig.ejs +8 -4
- package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
- package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/CreateEndpoint_Main.ejs +4 -4
- package/src/emit/application/crud/templates/CreateLogicalLib.ejs +74 -15
- package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/DeleteEndpoint_Main.ejs +4 -4
- package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
- package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
- package/src/emit/application/crud/templates/UpdateEndpoint_Main.ejs +4 -4
- package/src/emit/application/custom/customFlowPaths.js +13 -16
- package/src/emit/application/custom/customFlowS3.js +277 -66
- package/src/emit/application/custom/customFlows.js +131 -0
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +3 -1
- package/src/emit/application/custom/templates/FlowEndpoint_Main.ejs +2 -2
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +110 -0
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +110 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +89 -0
- package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
- package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
- package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
- package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +135 -27
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +197 -0
- package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +8 -1
- package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +107 -25
- package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +29 -9
- package/src/emit/application/rbac/templates/ReservedUsageTtlExpired_HdrDss.ejs +176 -0
- package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
- package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
- package/src/emit/infrastructure/functions/functionYamlArtifact.js +31 -8
- package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +21 -0
- package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
- package/src/emit/infrastructure/resources/iamResourceArtifacts.js +67 -5
- package/src/emit/infrastructure/resources/resourceState.js +15 -5
- package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
- package/src/emit/infrastructure/resources/templates/GeneratedLambdaRoleServiceConfig.ejs +5 -3
- package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
- package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
- package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +62 -1
- package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
- package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
- package/src/generate.js +32 -34
- package/src/output/mergeHooks.js +105 -5
- package/src/plan/domainRegistry.js +0 -10
- package/task.md +40 -96
- package/test/integration/graphDrivenFlowSchema.test.js +58 -0
- package/test/integration/publicApi.test.js +33 -24
- package/test/unit/application/createTargetRoleHandler.test.js +101 -9
- package/test/unit/application/crudApplicationArtifacts.test.js +4 -0
- package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
- package/test/unit/application/customFlowS3.test.js +730 -0
- package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
- package/test/unit/infrastructure/resourceArtifacts.test.js +103 -0
- package/test/unit/output/mergeHooks.test.js +137 -0
- package/test/unit/schemas/attributeTreeSchemas.test.js +32 -3
- package/test/unit/schemas/attributeTreeSingleService.test.js +175 -0
- package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
- package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
- package/test/unit/schemas/errorSchemas.test.js +84 -5
- package/test/unit/templates/hookTagsValidation.test.js +83 -0
- package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +0 -132
- package/test/unit/schemas/attributeTreeDirectives.test.js +0 -177
package/src/generate.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
1
4
|
import { buildGenerationPlan } from './plan/buildGenerationPlan.js';
|
|
2
5
|
import { resolveEmissionGroups } from './plan/domainRegistry.js';
|
|
3
6
|
import { commitArtifacts } from './output/commitArtifacts.js';
|
|
4
7
|
import { compileProject } from './compile/compileProject.js';
|
|
5
8
|
import {
|
|
6
9
|
loadLocalSchemaDirectory,
|
|
7
|
-
loadGenerateDirectives,
|
|
8
10
|
resolveExtendedObjectSchemas,
|
|
9
11
|
resolveGraphServiceTags
|
|
10
12
|
} from './compile/loadSchemas.js';
|
|
@@ -14,34 +16,32 @@ export async function generate(rootPath, options = {}) {
|
|
|
14
16
|
console.log(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
|
|
15
17
|
try {
|
|
16
18
|
const project = compileProject({ ...options, rootPath });
|
|
19
|
+
await mkdir(join(project.schemaRoot, 'ErrorSchemas'), {
|
|
20
|
+
recursive: true
|
|
21
|
+
});
|
|
17
22
|
const schemaLoadOptions = {
|
|
18
23
|
skipValidation: options.skipValidation
|
|
19
24
|
};
|
|
20
|
-
const [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
project,
|
|
37
|
-
'NotificationSchemas',
|
|
38
|
-
schemaLoadOptions
|
|
39
|
-
),
|
|
40
|
-
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions),
|
|
41
|
-
loadGenerateDirectives(project, schemaLoadOptions)
|
|
42
|
-
]);
|
|
25
|
+
const [rawObjects, relationships, flows, notifications, errors] =
|
|
26
|
+
await Promise.all([
|
|
27
|
+
loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
|
|
28
|
+
loadLocalSchemaDirectory(
|
|
29
|
+
project,
|
|
30
|
+
'RelationshipSchemas',
|
|
31
|
+
schemaLoadOptions
|
|
32
|
+
),
|
|
33
|
+
loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
|
|
34
|
+
loadLocalSchemaDirectory(
|
|
35
|
+
project,
|
|
36
|
+
'NotificationSchemas',
|
|
37
|
+
schemaLoadOptions
|
|
38
|
+
),
|
|
39
|
+
loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
|
|
40
|
+
]);
|
|
43
41
|
const schemaBucketName =
|
|
44
|
-
options.schemaBucketName ||
|
|
42
|
+
options.schemaBucketName ||
|
|
43
|
+
project.schemaBucketName ||
|
|
44
|
+
process.env.iz_serviceSchemaBucketName;
|
|
45
45
|
const objects = await resolveExtendedObjectSchemas(
|
|
46
46
|
rawObjects,
|
|
47
47
|
schemaBucketName
|
|
@@ -61,17 +61,17 @@ export async function generate(rootPath, options = {}) {
|
|
|
61
61
|
}
|
|
62
62
|
console.log(
|
|
63
63
|
`[INFO] [Generate] STATUS=SCHEMAS_LOADED | ` +
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
`objects=${objects.length}${objectSummary} ` +
|
|
65
|
+
`relationships=${relationships.length} flows=${flows.length} ` +
|
|
66
|
+
`notifications=${notifications.length} ` +
|
|
67
|
+
`errors=${errors.length}`
|
|
68
68
|
);
|
|
69
69
|
if (options.verbose || process.env.GENERATE_VERBOSE) {
|
|
70
70
|
for (const objectSchema of objects) {
|
|
71
71
|
const objectType = objectSchema.objectType || 'unknown';
|
|
72
72
|
console.log(
|
|
73
73
|
`[DEBUG] [Generate] OBJECT_SCHEMA | objectType=${objectType}\n` +
|
|
74
|
-
|
|
74
|
+
JSON.stringify(objectSchema, null, 2)
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -82,7 +82,6 @@ export async function generate(rootPath, options = {}) {
|
|
|
82
82
|
flows,
|
|
83
83
|
notifications,
|
|
84
84
|
errors,
|
|
85
|
-
directives,
|
|
86
85
|
objectGraphServiceTags,
|
|
87
86
|
relationshipGraphServiceTags,
|
|
88
87
|
logger: options.logger
|
|
@@ -111,17 +110,16 @@ export async function generate(rootPath, options = {}) {
|
|
|
111
110
|
const durationMs = Date.now() - startedAt;
|
|
112
111
|
console.log(
|
|
113
112
|
`[INFO] [Generate] STATUS=FINISHED | ` +
|
|
114
|
-
|
|
113
|
+
`files=${files.length} durationMs=${durationMs}`
|
|
115
114
|
);
|
|
116
115
|
|
|
117
116
|
return { project, durationMs, files };
|
|
118
117
|
} catch (error) {
|
|
119
118
|
console.log(
|
|
120
119
|
`[ERROR] [Generate] STATUS=FAILED | ` +
|
|
121
|
-
|
|
120
|
+
`durationMs=${Date.now() - startedAt}`,
|
|
122
121
|
error
|
|
123
122
|
);
|
|
124
123
|
throw error;
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
|
-
|
package/src/output/mergeHooks.js
CHANGED
|
@@ -40,6 +40,14 @@ async function mergeHook(outputRoot, artifact) {
|
|
|
40
40
|
return artifact.content;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
_validateHookIntegrity(artifact.path, sourceContent);
|
|
44
|
+
_warnOrphanedHooks(
|
|
45
|
+
artifact.path,
|
|
46
|
+
sourceContent,
|
|
47
|
+
artifact.content,
|
|
48
|
+
matchers
|
|
49
|
+
);
|
|
50
|
+
|
|
43
51
|
const tagOccurrences = new Map();
|
|
44
52
|
return artifact.content.replace(
|
|
45
53
|
matchers.anyTag,
|
|
@@ -60,13 +68,105 @@ async function mergeHook(outputRoot, artifact) {
|
|
|
60
68
|
}
|
|
61
69
|
const hookBlock = matches[count];
|
|
62
70
|
if (hookBlock !== null && hookBlock !== undefined) {
|
|
63
|
-
return hookBlock;
|
|
71
|
+
return _normalizeHookBlock(hookBlock, tagName);
|
|
64
72
|
}
|
|
65
73
|
return _match;
|
|
66
74
|
}
|
|
67
75
|
);
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
function _validateHookIntegrity(filePath, content) {
|
|
79
|
+
const lower = filePath.toLowerCase();
|
|
80
|
+
let openCount = 0;
|
|
81
|
+
let closeCount = 0;
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
lower.endsWith('.js') ||
|
|
85
|
+
lower.endsWith('.mjs') ||
|
|
86
|
+
lower.endsWith('.cjs')
|
|
87
|
+
) {
|
|
88
|
+
const openP1 = content.match(/\/\/\s*\(<\s*[\w.-]+/g);
|
|
89
|
+
const closeP1 = content.match(/\/\/\s*\(<\/\s*[\w.-]+/g);
|
|
90
|
+
const openP2 = content.match(/\/\/\s*<#\s*[\w.-]+/g);
|
|
91
|
+
const closeP2 = content.match(/\/\/\s*<#\/\s*[\w.-]+/g);
|
|
92
|
+
|
|
93
|
+
if (openP1) {
|
|
94
|
+
openCount += openP1.length;
|
|
95
|
+
}
|
|
96
|
+
if (closeP1) {
|
|
97
|
+
closeCount += closeP1.length;
|
|
98
|
+
}
|
|
99
|
+
if (openP2) {
|
|
100
|
+
openCount += openP2.length;
|
|
101
|
+
}
|
|
102
|
+
if (closeP2) {
|
|
103
|
+
closeCount += closeP2.length;
|
|
104
|
+
}
|
|
105
|
+
} else if (lower.endsWith('.yml') || lower.endsWith('.yaml')) {
|
|
106
|
+
const openYml = content.match(/#\s*<#\s*[\w.-]+/g);
|
|
107
|
+
const closeYml = content.match(/#\s*<#\/\s*[\w.-]+/g);
|
|
108
|
+
|
|
109
|
+
if (openYml) {
|
|
110
|
+
openCount += openYml.length;
|
|
111
|
+
}
|
|
112
|
+
if (closeYml) {
|
|
113
|
+
closeCount += closeYml.length;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (openCount !== closeCount) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`[mergeHooks] Malformed hook tags in '${filePath}': found ` +
|
|
120
|
+
`${openCount} open tag(s) but ${closeCount} close tag(s). ` +
|
|
121
|
+
`Aborting to prevent code loss.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function _warnOrphanedHooks(
|
|
127
|
+
filePath,
|
|
128
|
+
sourceContent,
|
|
129
|
+
artifactContent,
|
|
130
|
+
matchers
|
|
131
|
+
) {
|
|
132
|
+
const anyTagRegex = new RegExp(matchers.anyTag.source, 'gm');
|
|
133
|
+
let match = anyTagRegex.exec(sourceContent);
|
|
134
|
+
|
|
135
|
+
while (match !== null) {
|
|
136
|
+
const tagName = _extractTagName(match[1], match[3]);
|
|
137
|
+
let body = '';
|
|
138
|
+
if (typeof match[2] === 'string') {
|
|
139
|
+
body = match[2];
|
|
140
|
+
} else if (typeof match[4] === 'string') {
|
|
141
|
+
body = match[4];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (tagName && body.trim().length > 0) {
|
|
145
|
+
const templatePattern = matchers.tagPattern(tagName);
|
|
146
|
+
const existsInTemplate = templatePattern.test(artifactContent);
|
|
147
|
+
if (!existsInTemplate) {
|
|
148
|
+
console.warn(
|
|
149
|
+
`[WARN] [mergeHooks] Hook '${tagName}' in '${filePath}' ` +
|
|
150
|
+
`contains custom code but is no longer present in template.`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
match = anyTagRegex.exec(sourceContent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function _normalizeHookBlock(hookBlock, tagName) {
|
|
159
|
+
if (typeof hookBlock !== 'string' || hookBlock.length === 0) {
|
|
160
|
+
return hookBlock;
|
|
161
|
+
}
|
|
162
|
+
const esc = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
163
|
+
const openRegex = new RegExp(`(//\\s*\\(<\\s*${esc}\\s*)(?!>)\\)`);
|
|
164
|
+
const closeRegex = new RegExp(`(//\\s*\\(<\\/\\s*${esc}\\s*)(?!>)\\)`);
|
|
165
|
+
let normalized = hookBlock.replace(openRegex, '$1>)');
|
|
166
|
+
normalized = normalized.replace(closeRegex, '$1>)');
|
|
167
|
+
return normalized;
|
|
168
|
+
}
|
|
169
|
+
|
|
70
170
|
function _extractTagName(p1, p2) {
|
|
71
171
|
if (typeof p1 === 'string' && p1.length > 0) {
|
|
72
172
|
return p1;
|
|
@@ -85,8 +185,8 @@ function hookMatchers(filePath) {
|
|
|
85
185
|
lower.endsWith('.cjs')
|
|
86
186
|
) {
|
|
87
187
|
const jsP1 =
|
|
88
|
-
'\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s
|
|
89
|
-
'\\/\\/\\s*\\(<\\/\\s*\\1\\s
|
|
188
|
+
'\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s*>?\\s*\\)([\\s\\S]*?)' +
|
|
189
|
+
'\\/\\/\\s*\\(<\\/\\s*\\1\\s*>?\\s*\\)';
|
|
90
190
|
const jsP2 =
|
|
91
191
|
'\\/\\/\\s*<#\\s*([\\w.-]+)\\s*#>([\\s\\S]*?)' +
|
|
92
192
|
'\\/\\/\\s*<#\\/\\s*\\3\\s*#>';
|
|
@@ -95,8 +195,8 @@ function hookMatchers(filePath) {
|
|
|
95
195
|
tagPattern: (name) => {
|
|
96
196
|
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
97
197
|
const p1 =
|
|
98
|
-
`\\/\\/\\s*\\(<\\s*${esc}\\s
|
|
99
|
-
`\\/\\/\\s*\\(<\\/\\s*${esc}\\s
|
|
198
|
+
`\\/\\/\\s*\\(<\\s*${esc}\\s*>?\\s*\\)([\\s\\S]*?)` +
|
|
199
|
+
`\\/\\/\\s*\\(<\\/\\s*${esc}\\s*>?\\s*\\)`;
|
|
100
200
|
const p2 =
|
|
101
201
|
`\\/\\/\\s*<#\\s*${esc}\\s*#>([\\s\\S]*?)` +
|
|
102
202
|
`\\/\\/\\s*<#\\/\\s*${esc}\\s*#>`;
|
|
@@ -52,10 +52,6 @@ import {
|
|
|
52
52
|
attributeTreeObjectSchemaManagedPaths,
|
|
53
53
|
attributeTreeRelationshipSchemaManagedPaths
|
|
54
54
|
} from '../emit/schemas/attributeTree/emitAttributeTreeSchemas.js';
|
|
55
|
-
import {
|
|
56
|
-
emitAttributeTreeTargetValueStandard,
|
|
57
|
-
attributeTreeTargetValueStandardManagedPaths
|
|
58
|
-
} from '../emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js';
|
|
59
55
|
import {
|
|
60
56
|
emitLedgerObjectSchemas,
|
|
61
57
|
emitLedgerRelationshipSchemas,
|
|
@@ -81,7 +77,6 @@ export async function resolveEmissionGroups(context) {
|
|
|
81
77
|
flows,
|
|
82
78
|
notifications,
|
|
83
79
|
errors,
|
|
84
|
-
directives = [],
|
|
85
80
|
objectGraphServiceTags,
|
|
86
81
|
relationshipGraphServiceTags,
|
|
87
82
|
logger
|
|
@@ -206,11 +201,6 @@ export async function resolveEmissionGroups(context) {
|
|
|
206
201
|
managedPaths: rbacFlowSchemaManagedPaths(objects),
|
|
207
202
|
artifacts: rbacFlowArtifacts
|
|
208
203
|
},
|
|
209
|
-
{
|
|
210
|
-
scope: 'attribute-tree-target-value-standard',
|
|
211
|
-
managedPaths: attributeTreeTargetValueStandardManagedPaths(directives),
|
|
212
|
-
artifacts: emitAttributeTreeTargetValueStandard({ directives })
|
|
213
|
-
},
|
|
214
204
|
{
|
|
215
205
|
scope: 'attribute-tree-object-schemas',
|
|
216
206
|
managedPaths: attributeTreeObjectSchemaManagedPaths(objects),
|
package/task.md
CHANGED
|
@@ -1,132 +1,76 @@
|
|
|
1
|
-
# Task Checklist: Attribute Tree
|
|
1
|
+
# Task Checklist: Attribute Tree Single-Service Code Generation Consolidation
|
|
2
2
|
|
|
3
3
|
> **Canonical Architecture Plan**: [[10.Systems/GenerateServiceCode/30-Generation/Task Roadmap - Attribute Tree Per-Repo Generation Refactoring|Task Roadmap in Obsidian]]
|
|
4
|
-
> **Selected Design**: **
|
|
4
|
+
> **Selected Design**: **Single Main Service (Self-Repo Consolidation)** + **Style 1 (`generatedCode/<domain>/`)**
|
|
5
5
|
> **Repository**: `IzaraCore-SupportServices-GenerateServiceCode-Seagame`
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## 🎯 Target Architecture:
|
|
9
|
+
## 🎯 Target Architecture: Single Unified Service (Self-Repo)
|
|
10
|
+
|
|
11
|
+
All schemas for an `attributeTree` (Tree nodes, Standard Value Handler node, and cascading PropertyValue nodes) are emitted directly inside the **Main Service** workspace:
|
|
10
12
|
|
|
11
|
-
### Target Service (e.g. `SellOfferTermValueStandard`)
|
|
12
13
|
```text
|
|
13
14
|
app/src/schemas/
|
|
14
|
-
├── GenerateDirectives/
|
|
15
|
-
│ └── sellOfferTermValueStandard.js <-- [Directive] Developer creates this
|
|
16
|
-
│
|
|
17
15
|
├── ObjectSchemas/
|
|
16
|
+
│ ├── sellOffer.js <-- Source ObjectSchema
|
|
18
17
|
│ └── generatedCode/
|
|
19
18
|
│ ├── attributeTree/
|
|
20
|
-
│ │
|
|
21
|
-
│
|
|
22
|
-
│
|
|
23
|
-
│
|
|
19
|
+
│ │ ├── sellOfferTermLink.js <-- Link Node
|
|
20
|
+
│ │ ├── sellOfferTermValue.js <-- Value Node
|
|
21
|
+
│ │ ├── sellOfferTermLabel.js <-- Label Node
|
|
22
|
+
│ │ └── sellOfferTermValueStandard.js <-- Handler Node (Self-Repo)
|
|
23
|
+
│ │
|
|
24
|
+
│ └── propertyValue/ <-- Cascading from ValueStandard
|
|
25
|
+
│ ├── sellOfferTermValueStandardPropertyLabel.js
|
|
26
|
+
│ └── sellOfferTermValueStandardPropertyValue.js
|
|
24
27
|
│
|
|
25
28
|
└── RelationshipSchemas/
|
|
26
29
|
└── generatedCode/
|
|
30
|
+
├── attributeTree/
|
|
31
|
+
│ ├── hasSellOfferTermLink.js
|
|
32
|
+
│ ├── disabledSellOfferTermLink.js
|
|
33
|
+
│ ├── isSellOfferTerm.js
|
|
34
|
+
│ └── isSellOfferTermLabel.js
|
|
35
|
+
│
|
|
27
36
|
└── propertyValue/
|
|
28
37
|
├── hasSellOfferTermValueStandardPropertyValue.js
|
|
29
38
|
├── disabledSellOfferTermValueStandardPropertyValue.js
|
|
30
39
|
└── isSellOfferTermValueStandardPropertyLabel.js
|
|
31
40
|
```
|
|
32
41
|
|
|
33
|
-
### Source Service (Self-Repo)
|
|
34
|
-
```text
|
|
35
|
-
app/src/schemas/
|
|
36
|
-
├── ObjectSchemas/
|
|
37
|
-
│ ├── sellOffer.js <-- Source ObjectSchema
|
|
38
|
-
│ └── generatedCode/
|
|
39
|
-
│ └── attributeTree/
|
|
40
|
-
│ ├── sellOfferTermLink.js
|
|
41
|
-
│ ├── sellOfferTermValue.js
|
|
42
|
-
│ └── sellOfferTermLabel.js
|
|
43
|
-
│
|
|
44
|
-
└── RelationshipSchemas/
|
|
45
|
-
└── generatedCode/
|
|
46
|
-
└── attributeTree/
|
|
47
|
-
├── hasSellOfferTermLink.js
|
|
48
|
-
├── disabledSellOfferTermLink.js
|
|
49
|
-
├── isSellOfferTerm.js
|
|
50
|
-
└── isSellOfferTermLabel.js
|
|
51
|
-
```
|
|
52
|
-
|
|
53
42
|
---
|
|
54
43
|
|
|
55
44
|
## 📋 Implementation Checklist
|
|
56
45
|
|
|
57
46
|
- [x] **TASK-01: Recursive Schema Loading & Directives Loader**
|
|
58
47
|
- **File**: `src/compile/loadSchemas.js`
|
|
59
|
-
- **
|
|
60
|
-
1. Update `loadLocalSchemaDirectory(project, directoryName, options)` to recursively find all `.js` files within subdirectories (supporting `ObjectSchemas/generatedCode/**` and `RelationshipSchemas/generatedCode/**`).
|
|
61
|
-
2. Export `loadGenerateDirectives(project, options)` calling `loadLocalSchemaDirectory(project, 'GenerateDirectives', options)`.
|
|
62
|
-
- **Acceptance Criteria**: `loadLocalSchemaDirectory(project, 'ObjectSchemas')` discovers and validates schemas nested inside `generatedCode/attributeTree/` and `generatedCode/propertyValue/`. Returns `[]` when `GenerateDirectives` folder is missing.
|
|
48
|
+
- **Acceptance Criteria**: `loadLocalSchemaDirectory(project, 'ObjectSchemas')` discovers and validates schemas nested inside `generatedCode/**`.
|
|
63
49
|
|
|
64
|
-
- [x] **TASK-02:
|
|
65
|
-
- **File**: `src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js` (NEW)
|
|
66
|
-
- **Action**:
|
|
67
|
-
1. Filter `directives` for `directive.type === 'attributeTreeTarget'`.
|
|
68
|
-
2. Emit `{tag}ValueStandard.js` to `app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}ValueStandard.js`.
|
|
69
|
-
3. Include preservation hook `//(<addOnDataStructure${tag}ValueStandard>)` with commented `propertyValue` addOn template.
|
|
70
|
-
4. Export `attributeTreeTargetValueStandardManagedPaths(directives)`.
|
|
71
|
-
- **Acceptance Criteria**: Emitted file adheres strictly to Izara backend syntax rules and matches Style 1 output path.
|
|
72
|
-
|
|
73
|
-
- [x] **TASK-03: Align AttributeTree Self-Repo Emitter to Style 1**
|
|
50
|
+
- [x] **TASK-02: Consolidate ValueStandard into `emitAttributeTreeSchemas.js`**
|
|
74
51
|
- **File**: `src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js`
|
|
75
52
|
- **Action**:
|
|
76
|
-
1.
|
|
77
|
-
2.
|
|
78
|
-
3.
|
|
79
|
-
- **Acceptance Criteria**:
|
|
80
|
-
|
|
81
|
-
- [x] **TASK-04: Align PropertyValue Emitter to Style 1**
|
|
82
|
-
- **File**: `src/emit/schemas/propertyValue/emitPropertyValueSchemas.js`
|
|
83
|
-
- **Action**:
|
|
84
|
-
1. Update ObjectSchema emission paths from `generatedPropertyValue/` to `generatedCode/propertyValue/`.
|
|
85
|
-
2. Update RelationshipSchema emission paths from `generatedPropertyValue/` to `generatedCode/propertyValue/`.
|
|
86
|
-
3. Update `propertyValueObjectSchemaManagedPaths` and `propertyValueRelationshipSchemaManagedPaths`.
|
|
87
|
-
- **Acceptance Criteria**: PropertyValue schemas output cleanly into `generatedCode/propertyValue/`.
|
|
88
|
-
|
|
89
|
-
- [x] **TASK-05: Register Managed Scopes in Domain Registry**
|
|
90
|
-
- **File**: `src/plan/domainRegistry.js`
|
|
91
|
-
- **Action**:
|
|
92
|
-
1. Import `emitAttributeTreeTargetValueStandard` and `attributeTreeTargetValueStandardManagedPaths`.
|
|
93
|
-
2. Register new scope `attribute-tree-target-value-standard`.
|
|
94
|
-
3. Pass `directives` into `resolveEmissionGroups(context)`.
|
|
95
|
-
- **Acceptance Criteria**: All 5 dynamic schema scopes registered and tracked in `.generate-service-code-v2-managed.json`.
|
|
96
|
-
|
|
97
|
-
- [x] **TASK-06: Decouple Generation Pipeline in `src/generate.js`**
|
|
98
|
-
- **File**: `src/generate.js`
|
|
99
|
-
- **Action**:
|
|
100
|
-
1. Load directives: `const directives = await loadGenerateDirectives(project, schemaLoadOptions);`.
|
|
101
|
-
2. Pass `directives` to `resolveEmissionGroups`.
|
|
102
|
-
3. Delete lines 104-111 (`targetFiles = await _emitTargetServiceAttributeTreeSchemas(...)`).
|
|
103
|
-
4. Delete lines 129-186 (`_emitTargetServiceAttributeTreeSchemas` function definition).
|
|
104
|
-
- **Acceptance Criteria**: Zero cross-repo filesystem writes. Generator operates strictly within `workspaceOutputRoot`.
|
|
105
|
-
|
|
106
|
-
- [x] **TASK-07: Delete Legacy Cross-Repo Files**
|
|
107
|
-
- **Files to Delete**:
|
|
108
|
-
- `src/emit/schemas/attributeTree/resolveTargetServicePath.js`
|
|
109
|
-
- `src/emit/schemas/attributeTree/emitAttributeTreeTargetSchemas.js`
|
|
110
|
-
- **Action**: Remove dead files and any unused imports.
|
|
111
|
-
- **Acceptance Criteria**: `git status` shows clean file deletions.
|
|
53
|
+
1. Emit `${tag}ValueStandard.js` directly within `emitAttributeTreeObjectSchemas`.
|
|
54
|
+
2. Add `${tag}ValueStandard.js` to `attributeTreeObjectSchemaManagedPaths`.
|
|
55
|
+
3. Include preservation hook `//(<addOnDataStructure${tag}ValueStandard>)` with commented `propertyValue` addOn template.
|
|
56
|
+
- **Acceptance Criteria**: Single-service attributeTree generates all 4 ObjectSchemas (`Link`, `Value`, `Label`, `ValueStandard`).
|
|
112
57
|
|
|
113
|
-
- [x] **TASK-
|
|
58
|
+
- [x] **TASK-03: Remove Target Directive Scope & Dead Code**
|
|
114
59
|
- **Files**:
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
117
|
-
- `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- **Acceptance Criteria**:
|
|
126
|
-
|
|
127
|
-
- [x] **TASK-
|
|
60
|
+
- `src/plan/domainRegistry.js` (Removed `attribute-tree-target-value-standard` scope & unused `directives` param)
|
|
61
|
+
- `src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js` (Deleted dead file)
|
|
62
|
+
- `src/compile/loadSchemas.js` (Removed obsolete `loadGenerateDirectives`)
|
|
63
|
+
- `src/generate.js` (Removed `loadGenerateDirectives` call & `directives` passing)
|
|
64
|
+
- `test/unit/schemas/attributeTreeSingleService.test.js` (Removed obsolete directive loader tests)
|
|
65
|
+
- **Acceptance Criteria**: Clean scope tracking under `attribute-tree-object-schemas` with zero dead directive loading.
|
|
66
|
+
|
|
67
|
+
- [x] **TASK-04: Single-Service & Cascading Integration Verification**
|
|
68
|
+
- **File**: `test/unit/schemas/attributeTreeSingleService.test.js`
|
|
69
|
+
- **Action**: Verifies single-service generation and cascading generation to `propertyValue` within the same repository.
|
|
70
|
+
- **Acceptance Criteria**: 100% passing tests with zero cross-repo write or external directive needed.
|
|
71
|
+
|
|
72
|
+
- [x] **TASK-05: Synchronize Vault & Documentation**
|
|
128
73
|
- **Vault Files**:
|
|
129
74
|
- `10.Systems/GenerateServiceCode/20-Schema/Dynamic Schema.md`
|
|
130
75
|
- `40.Vault/Operations/timeline-log.md`
|
|
131
76
|
- `40.Vault/Memory/active-context.md`
|
|
132
|
-
- **Taiga Wiki**: Update `generatecode` wiki page.
|
|
@@ -544,6 +544,64 @@ test('validateFlowSchema passes for s3 event with valid bucketName', () => {
|
|
|
544
544
|
);
|
|
545
545
|
});
|
|
546
546
|
|
|
547
|
+
test('validateFlowSchema passes for s3 event with explicit S3 flowSteps', () => {
|
|
548
|
+
assert.equal(
|
|
549
|
+
validateFlowSchema({
|
|
550
|
+
flowTag: 'uploadFlow',
|
|
551
|
+
event: ['s3'],
|
|
552
|
+
bucketName: 'MyUploadBucket',
|
|
553
|
+
flowSteps: [
|
|
554
|
+
{ stepName: 'CreateObject', event: ['wbs'] },
|
|
555
|
+
{ stepName: 'ReservedLimit', event: ['sqs'] },
|
|
556
|
+
{ stepName: 'CreatePreSignUrl', event: ['sqs'] },
|
|
557
|
+
{ stepName: 'ProcessAfterUpload', event: ['s3'] },
|
|
558
|
+
{ stepName: 'ConfirmAfterUpload', event: ['sqs'] }
|
|
559
|
+
]
|
|
560
|
+
}),
|
|
561
|
+
true
|
|
562
|
+
);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test('validateFlowSchema passes for s3 event without flowSteps', () => {
|
|
566
|
+
assert.equal(
|
|
567
|
+
validateFlowSchema({
|
|
568
|
+
flowTag: 'uploadFlow',
|
|
569
|
+
event: ['s3'],
|
|
570
|
+
bucketName: 'MyUploadBucket'
|
|
571
|
+
}),
|
|
572
|
+
true
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
test('validateFlowSchema rejects s3 event with invalid entry stepName', () => {
|
|
577
|
+
assert.throws(
|
|
578
|
+
() =>
|
|
579
|
+
validateFlowSchema({
|
|
580
|
+
flowTag: 'uploadFlow',
|
|
581
|
+
event: ['s3'],
|
|
582
|
+
bucketName: 'MyUploadBucket',
|
|
583
|
+
flowSteps: [{ stepName: 'InvalidEntry' }]
|
|
584
|
+
}),
|
|
585
|
+
/Entry step flowSteps\[0\] for s3 flow must have stepName 'CreateObject' or 'Start'/
|
|
586
|
+
);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test('validateFlowSchema rejects s3 event with invalid non-entry stepName', () => {
|
|
590
|
+
assert.throws(
|
|
591
|
+
() =>
|
|
592
|
+
validateFlowSchema({
|
|
593
|
+
flowTag: 'uploadFlow',
|
|
594
|
+
event: ['s3'],
|
|
595
|
+
bucketName: 'MyUploadBucket',
|
|
596
|
+
flowSteps: [
|
|
597
|
+
{ stepName: 'CreateObject' },
|
|
598
|
+
{ stepName: 'InvalidStep' }
|
|
599
|
+
]
|
|
600
|
+
}),
|
|
601
|
+
/is invalid for s3 flow/
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
|
|
547
605
|
test('validateFlowSchema rejects event with storedCache or triggerCache as invalid event', () => {
|
|
548
606
|
assert.throws(
|
|
549
607
|
() =>
|
|
@@ -23,7 +23,7 @@ test('generate commits schemas, application artifacts, and resources in one publ
|
|
|
23
23
|
const rootPath = await createCrudProject();
|
|
24
24
|
const result = await generate(rootPath);
|
|
25
25
|
|
|
26
|
-
assert.strictEqual(result.files.length,
|
|
26
|
+
assert.strictEqual(result.files.length, 59);
|
|
27
27
|
assert.equal(result.project.rootPath, rootPath);
|
|
28
28
|
assert.match(
|
|
29
29
|
await readFile(
|
|
@@ -366,7 +366,7 @@ test('generate removes stale CRUD schemas and application artifacts', async () =
|
|
|
366
366
|
|
|
367
367
|
const result = await generate(rootPath);
|
|
368
368
|
|
|
369
|
-
assert.equal(result.files.length,
|
|
369
|
+
assert.equal(result.files.length, 23);
|
|
370
370
|
await assert.rejects(
|
|
371
371
|
readFile(
|
|
372
372
|
join(
|
|
@@ -448,36 +448,45 @@ async function createTriggerCacheProject() {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
test(
|
|
451
|
-
'emits ignoreExternalServices hooks in
|
|
452
|
-
'commands/uploadSchema.js',
|
|
451
|
+
'emits ignoreExternalServices hooks in commands/uploadSchema.js',
|
|
453
452
|
async () => {
|
|
454
453
|
const rootPath = await createCrudProject();
|
|
455
454
|
await generate(rootPath);
|
|
456
455
|
|
|
457
|
-
const uploadSchemaMain = await readFile(
|
|
456
|
+
// const uploadSchemaMain = await readFile(
|
|
457
|
+
// join(
|
|
458
|
+
// rootPath,
|
|
459
|
+
// 'app/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
|
|
460
|
+
// ),
|
|
461
|
+
// 'utf8'
|
|
462
|
+
// );
|
|
463
|
+
// assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
|
|
464
|
+
// assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
|
|
465
|
+
|
|
466
|
+
// const coreResource = await readFile(
|
|
467
|
+
// join(
|
|
468
|
+
// rootPath,
|
|
469
|
+
// 'app/sls_yaml/generatedCode/source/core-resource.yml'
|
|
470
|
+
// ),
|
|
471
|
+
// 'utf8'
|
|
472
|
+
// );
|
|
473
|
+
// assert.match(
|
|
474
|
+
// coreResource,
|
|
475
|
+
// /UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
|
|
476
|
+
// );
|
|
477
|
+
// assert.match(
|
|
478
|
+
// coreResource,
|
|
479
|
+
// /UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
|
|
480
|
+
// );
|
|
481
|
+
|
|
482
|
+
const uploadSchemaCommand = await readFile(
|
|
458
483
|
join(
|
|
459
484
|
rootPath,
|
|
460
|
-
'app/
|
|
485
|
+
'app/initial_setup/generatedCode/source/commands/uploadSchema.js'
|
|
461
486
|
),
|
|
462
487
|
'utf8'
|
|
463
488
|
);
|
|
464
|
-
assert.match(
|
|
465
|
-
assert.match(
|
|
466
|
-
|
|
467
|
-
const coreResource = await readFile(
|
|
468
|
-
join(
|
|
469
|
-
rootPath,
|
|
470
|
-
'app/sls_yaml/generatedCode/source/core-resource.yml'
|
|
471
|
-
),
|
|
472
|
-
'utf8'
|
|
473
|
-
);
|
|
474
|
-
assert.match(
|
|
475
|
-
coreResource,
|
|
476
|
-
/UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
|
|
477
|
-
);
|
|
478
|
-
assert.match(
|
|
479
|
-
coreResource,
|
|
480
|
-
/UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
|
|
481
|
-
);
|
|
489
|
+
assert.match(uploadSchemaCommand, /\/\/\(<ignoreExternalServices>\)/);
|
|
490
|
+
assert.match(uploadSchemaCommand, /\/\/\(<\/ignoreExternalServices>\)/);
|
|
482
491
|
}
|
|
483
492
|
);
|