@izara_project/izara-core-generate-service-code 1.0.57 → 1.0.58
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/__tests__/getIntTestConfig.test.js +49 -0
- package/src/codeGenerators/resource/sls_yaml/FlowResourceYamlGenerator.js +1 -1
- package/src/generate.js +40 -40
- package/src/generateCode.js +77 -68
- package/src/generateIntTest.js +11 -0
- package/src/generateResourceIntTest.js +9 -0
- package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
- package/src/generateResources/IntTest/libs/libs.js +10 -2
- package/src/generateSchema.js +78 -18
- package/src/schemaGenerators/app/src/schemas/ObjectSchemas/templates/DynamicAttributeTreeStandardSchemaTemplate.ejs +1 -1
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.58",
|
|
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": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import libs from '../generateResources/IntTest/libs/libs.js';
|
|
5
|
+
import { getObjectSchema } from '@izara_project/izara-core-library-service-schemas';
|
|
6
|
+
|
|
7
|
+
const { getIntTestConfig } = libs;
|
|
8
|
+
|
|
9
|
+
describe('getIntTestConfig path resolution', () => {
|
|
10
|
+
let originalGetDataFromPath;
|
|
11
|
+
let passedArgs;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
originalGetDataFromPath = getObjectSchema.getDataFromPath;
|
|
15
|
+
passedArgs = null;
|
|
16
|
+
getObjectSchema.getDataFromPath = async (_izContext, folderName, rootFolderPath, options) => {
|
|
17
|
+
passedArgs = { folderName, rootFolderPath, options };
|
|
18
|
+
return [];
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
getObjectSchema.getDataFromPath = originalGetDataFromPath;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('Case 1: should resolve correctly when rootPath includes generateIntegrationTest', async () => {
|
|
27
|
+
const rootPath = '/home/ironfist/Pong/Izara Project/Izara-Market-Product/izara-market-products-sell-offer-price-pong/app/src/generateIntegrationTest/';
|
|
28
|
+
await getIntTestConfig({ logger: console }, 'generateIntegrationTest/libs/', rootPath);
|
|
29
|
+
|
|
30
|
+
expect(passedArgs).not.toBeNull();
|
|
31
|
+
expect(passedArgs.folderName).toBe('libs');
|
|
32
|
+
expect(passedArgs.rootFolderPath).toBe('/home/ironfist/Pong/Izara Project/Izara-Market-Product/izara-market-products-sell-offer-price-pong/app/src/generateIntegrationTest');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('Case 2: should resolve correctly when rootPath is repo root with app/src/... structure', async () => {
|
|
36
|
+
const tmpDir = path.join(process.cwd(), 'src', '__tests__', 'scratch_tmp');
|
|
37
|
+
fs.mkdirSync(path.join(tmpDir, 'app', 'src', 'generateIntegrationTest', 'libs'), { recursive: true });
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await getIntTestConfig({ logger: console }, 'generateIntegrationTest/libs/', tmpDir);
|
|
41
|
+
|
|
42
|
+
expect(passedArgs).not.toBeNull();
|
|
43
|
+
expect(passedArgs.folderName).toBe('libs');
|
|
44
|
+
expect(passedArgs.rootFolderPath).toBe(path.join(tmpDir, 'app', 'src', 'generateIntegrationTest'));
|
|
45
|
+
} finally {
|
|
46
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -127,6 +127,7 @@ export async function generateFlowResourceYaml(allSchemas, options) {
|
|
|
127
127
|
|
|
128
128
|
const systemOwnTopicFlows = [
|
|
129
129
|
'ProcessLogical',
|
|
130
|
+
'PaginateProcessLogical',
|
|
130
131
|
'FindData',
|
|
131
132
|
'Create',
|
|
132
133
|
'Update',
|
|
@@ -169,7 +170,6 @@ export async function generateFlowResourceYaml(allSchemas, options) {
|
|
|
169
170
|
const systemQueues = [
|
|
170
171
|
'Register',
|
|
171
172
|
'WebSocket',
|
|
172
|
-
'PaginateProcessLogical',
|
|
173
173
|
'CreateComplete',
|
|
174
174
|
'UpdateComplete',
|
|
175
175
|
'DeleteComplete',
|
package/src/generate.js
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
Copyright (C) 2020 Sven Mason <http://izara.io>
|
|
3
3
|
|
|
4
4
|
This program is free software: you can redistribute it and/or modify
|
|
5
|
-
it under the terms of the GNU Affero General Public License as
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
(at your option) any later version.
|
|
8
8
|
|
|
9
9
|
This program is distributed in the hope that it will be useful,
|
|
10
10
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
12
|
GNU Affero General Public License for more details.
|
|
13
13
|
|
|
14
14
|
You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
along with this program.
|
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { join } from 'path';
|
|
@@ -24,53 +24,51 @@ import { generateSchema } from './generateSchema.js';
|
|
|
24
24
|
const { validateLocalSchema } = validator;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* High-level
|
|
28
|
-
*
|
|
27
|
+
* High-level API to validate schemas and generate code.
|
|
28
|
+
* Wires legacy Logger and validateLocalSchema.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @param {string}
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {boolean} [options.
|
|
38
|
-
* @param {
|
|
39
|
-
* @
|
|
40
|
-
* @returns {Promise<{ durationMs: number }>}
|
|
30
|
+
* @param {string} rootPath - The root path of the project.
|
|
31
|
+
* @param {object} [options={}] - Configuration options.
|
|
32
|
+
* @param {object} [options.logger] - Logger instance.
|
|
33
|
+
* @param {string} [options.configPath] - Path to the serverless configuration file.
|
|
34
|
+
* @param {string} [options.outputPath] - Output directory path.
|
|
35
|
+
* @param {boolean} [options.skipValidation=false] - Whether to skip schema validation.
|
|
36
|
+
* @param {boolean} [options.skipGenerateSchema=false] - Whether to skip schema generation.
|
|
37
|
+
* @param {boolean} [options.skipGenerateCode=false] - Whether to skip code generation.
|
|
38
|
+
* @param {Array} [options.schemaTemplateConfig] - Schema template configuration.
|
|
39
|
+
* @returns {Promise<{durationMs: number}>} Generation duration details.
|
|
41
40
|
*/
|
|
42
41
|
async function generate(rootPath, options = {}) {
|
|
43
42
|
const {
|
|
44
43
|
logger = createSilentLogger(),
|
|
45
44
|
configPath = join(rootPath, 'config', 'serverless.config.yml'),
|
|
45
|
+
outputPath = rootPath,
|
|
46
46
|
skipValidation = false,
|
|
47
47
|
skipGenerateSchema = false,
|
|
48
|
-
|
|
48
|
+
skipGenerateCode = false,
|
|
49
|
+
schemaTemplateConfig
|
|
49
50
|
} = options;
|
|
50
51
|
|
|
51
52
|
const schemasPath = join(rootPath, 'app', 'src', 'schemas');
|
|
52
53
|
|
|
53
|
-
// Auto-generate dynamic schemas before validation (unless skipped)
|
|
54
54
|
if (!skipGenerateSchema) {
|
|
55
|
-
await generateSchema(rootPath, {
|
|
55
|
+
await generateSchema(rootPath, {
|
|
56
|
+
configPath,
|
|
57
|
+
outputPath,
|
|
58
|
+
schemaTemplateConfig
|
|
59
|
+
});
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
// Validate schemas before generating (unless skipped)
|
|
59
62
|
if (!skipValidation) {
|
|
60
63
|
patchFlowStepsValidationSchema();
|
|
61
64
|
|
|
62
|
-
console.info('[INFO] [generate]
|
|
65
|
+
console.info('[INFO] [generate] STATUS=VALIDATING | local schemas...');
|
|
63
66
|
const validateStartTime = Date.now();
|
|
64
|
-
|
|
65
|
-
const result = await validateLocalSchema(
|
|
66
|
-
{ logger },
|
|
67
|
-
schemasPath,
|
|
68
|
-
configPath
|
|
69
|
-
);
|
|
70
|
-
|
|
67
|
+
const result = await validateLocalSchema({ logger }, schemasPath, configPath);
|
|
71
68
|
const validateDuration = Date.now() - validateStartTime;
|
|
69
|
+
|
|
72
70
|
console.info(
|
|
73
|
-
`[INFO] [generate]
|
|
71
|
+
`[INFO] [generate] STATUS=VALIDATED | errors=${result.validateErrors.length} | elapsedMs=${validateDuration}`
|
|
74
72
|
);
|
|
75
73
|
|
|
76
74
|
if (result.validateErrors.length > 0) {
|
|
@@ -84,20 +82,26 @@ async function generate(rootPath, options = {}) {
|
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
|
|
85
|
+
if (skipGenerateCode) {
|
|
86
|
+
return { durationMs: 0 };
|
|
87
|
+
}
|
|
88
|
+
|
|
87
89
|
const startTime = Date.now();
|
|
88
90
|
await generateCode(rootPath, { outputPath });
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return { durationMs };
|
|
91
|
+
return { durationMs: Date.now() - startTime };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function patchFlowStepsValidationSchema() {
|
|
95
95
|
const flowStepsSchema = basicValidatorSchema.basicFlowSchema?.properties?.flowSteps;
|
|
96
|
+
if (!flowStepsSchema || flowStepsSchema.oneOf) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
96
99
|
|
|
97
|
-
if (!flowStepsSchema || flowStepsSchema.oneOf) return;
|
|
98
100
|
const objectStepSchema = {
|
|
99
101
|
...flowStepsSchema.items,
|
|
100
|
-
required: (flowStepsSchema.items.required || []).filter(
|
|
102
|
+
required: (flowStepsSchema.items.required || []).filter(
|
|
103
|
+
item => item !== 'stepName'
|
|
104
|
+
)
|
|
101
105
|
};
|
|
102
106
|
|
|
103
107
|
basicValidatorSchema.basicFlowSchema.properties.flowSteps = {
|
|
@@ -114,10 +118,6 @@ function patchFlowStepsValidationSchema() {
|
|
|
114
118
|
};
|
|
115
119
|
}
|
|
116
120
|
|
|
117
|
-
/**
|
|
118
|
-
* Creates a silent logger that only outputs errors.
|
|
119
|
-
* @returns {Object} Logger interface
|
|
120
|
-
*/
|
|
121
121
|
function createSilentLogger() {
|
|
122
122
|
return {
|
|
123
123
|
debug: () => {},
|
package/src/generateCode.js
CHANGED
|
@@ -37,6 +37,14 @@ function createGeneratorOptions(rootPath, options = {}) {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Low-level generator to generate application code files from all schemas.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} rootPath - The root path of the project.
|
|
44
|
+
* @param {object} [options={}] - Configuration options.
|
|
45
|
+
* @param {string} [options.outputPath] - Output directory path.
|
|
46
|
+
* @returns {Promise<void>}
|
|
47
|
+
*/
|
|
40
48
|
export async function generateCode(rootPath, options = {}) {
|
|
41
49
|
console.log(
|
|
42
50
|
'[INFO] [generateCode] STATUS=STARTING | Generating code files from all schemas...'
|
|
@@ -130,6 +138,57 @@ export async function generateCode(rootPath, options = {}) {
|
|
|
130
138
|
);
|
|
131
139
|
collectHookBackups(outputPath);
|
|
132
140
|
|
|
141
|
+
function processSourceHooks(currentDir) {
|
|
142
|
+
if (!fs.existsSync(currentDir)) return;
|
|
143
|
+
|
|
144
|
+
const items = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
145
|
+
const hasSource = items.some(
|
|
146
|
+
item => item.isDirectory() && item.name === 'source'
|
|
147
|
+
);
|
|
148
|
+
const hasHook = items.some(
|
|
149
|
+
item => item.isDirectory() && item.name === 'hook'
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (hasSource && hasHook) {
|
|
153
|
+
const sourcePath = path.join(currentDir, 'source');
|
|
154
|
+
const hookPath = path.join(currentDir, 'hook');
|
|
155
|
+
const hookItems = fs.readdirSync(hookPath, { withFileTypes: true });
|
|
156
|
+
|
|
157
|
+
for (const hookItem of hookItems) {
|
|
158
|
+
if (!hookItem.isFile()) continue;
|
|
159
|
+
|
|
160
|
+
const hookFile = path.join(hookPath, hookItem.name);
|
|
161
|
+
const sourceFile = path.join(sourcePath, hookItem.name);
|
|
162
|
+
|
|
163
|
+
if (!fs.existsSync(sourceFile)) {
|
|
164
|
+
fs.copyFileSync(hookFile, sourceFile);
|
|
165
|
+
console.log(
|
|
166
|
+
`[INFO] [generateCode] HOOK APPLIED: Created ${hookItem.name} from hook.`
|
|
167
|
+
);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const mergedContent = mergeHookIntoSource(
|
|
172
|
+
fs.readFileSync(sourceFile, 'utf8'),
|
|
173
|
+
fs.readFileSync(hookFile, 'utf8'),
|
|
174
|
+
sourceFile
|
|
175
|
+
);
|
|
176
|
+
fs.writeFileSync(sourceFile, mergedContent);
|
|
177
|
+
console.log(
|
|
178
|
+
`[INFO] [generateCode] HOOK APPLIED: Merged ${hookItem.name} into source.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
} else if (hasSource) {
|
|
182
|
+
fs.mkdirSync(path.join(currentDir, 'hook'), { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const item of items) {
|
|
186
|
+
if (!item.isDirectory() || item.name === 'source' || item.name === 'hook')
|
|
187
|
+
continue;
|
|
188
|
+
processSourceHooks(path.join(currentDir, item.name));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
133
192
|
// ponytail: ensure hooks are restored even if generation fails
|
|
134
193
|
try {
|
|
135
194
|
console.log(
|
|
@@ -204,80 +263,30 @@ export async function generateCode(rootPath, options = {}) {
|
|
|
204
263
|
await generateSharedResourceYaml(allSchemas, generatorOptions);
|
|
205
264
|
await generateInitialSetup(allSchemas, generatorOptions);
|
|
206
265
|
|
|
207
|
-
// 3. Hook Integration
|
|
208
|
-
console.log(
|
|
209
|
-
'[INFO] [generateCode] STATUS=HOOKS | Processing hook folders beside source folders...'
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
function processSourceHooks(currentDir) {
|
|
213
|
-
if (!fs.existsSync(currentDir)) return;
|
|
214
|
-
|
|
215
|
-
const items = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
216
|
-
const hasSource = items.some(
|
|
217
|
-
item => item.isDirectory() && item.name === 'source'
|
|
218
|
-
);
|
|
219
|
-
const hasHook = items.some(
|
|
220
|
-
item => item.isDirectory() && item.name === 'hook'
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
if (hasSource && hasHook) {
|
|
224
|
-
const sourcePath = path.join(currentDir, 'source');
|
|
225
|
-
const hookPath = path.join(currentDir, 'hook');
|
|
226
|
-
const hookItems = fs.readdirSync(hookPath, { withFileTypes: true });
|
|
227
266
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const mergedContent = mergeHookIntoSource(
|
|
243
|
-
fs.readFileSync(sourceFile, 'utf8'),
|
|
244
|
-
fs.readFileSync(hookFile, 'utf8'),
|
|
245
|
-
sourceFile
|
|
246
|
-
);
|
|
247
|
-
fs.writeFileSync(sourceFile, mergedContent);
|
|
248
|
-
console.log(
|
|
249
|
-
`[INFO] [generateCode] HOOK APPLIED: Merged ${hookItem.name} into source.`
|
|
250
|
-
);
|
|
267
|
+
} finally {
|
|
268
|
+
// Restore backed-up hooks
|
|
269
|
+
if (hookDirs.size > 0 || hookBackups.length > 0) {
|
|
270
|
+
console.log(
|
|
271
|
+
`[INFO] [generateCode] STATUS=RESTORE | Restoring ${hookBackups.length} hook files...`
|
|
272
|
+
);
|
|
273
|
+
for (const relativeHookDir of hookDirs) {
|
|
274
|
+
fs.mkdirSync(path.join(outputPath, relativeHookDir), { recursive: true });
|
|
275
|
+
}
|
|
276
|
+
for (const backup of hookBackups) {
|
|
277
|
+
const hookDir = path.join(outputPath, backup.relativePath);
|
|
278
|
+
fs.writeFileSync(path.join(hookDir, backup.name), backup.content);
|
|
251
279
|
}
|
|
252
|
-
} else if (hasSource) {
|
|
253
|
-
fs.mkdirSync(path.join(currentDir, 'hook'), { recursive: true });
|
|
254
280
|
}
|
|
255
281
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
}
|
|
282
|
+
console.log(
|
|
283
|
+
'[INFO] [generateCode] STATUS=HOOKS | Processing hook folders beside source folders...'
|
|
284
|
+
);
|
|
285
|
+
processSourceHooks(generatorOptions.outputPath);
|
|
262
286
|
|
|
263
|
-
} finally {
|
|
264
|
-
// Restore backed-up hooks
|
|
265
|
-
if (hookDirs.size > 0 || hookBackups.length > 0) {
|
|
266
287
|
console.log(
|
|
267
|
-
|
|
288
|
+
'[INFO] [generateCode] STATUS=FINISHED | Code generation complete.'
|
|
268
289
|
);
|
|
269
|
-
for (const relativeHookDir of hookDirs) {
|
|
270
|
-
fs.mkdirSync(path.join(outputPath, relativeHookDir), { recursive: true });
|
|
271
|
-
}
|
|
272
|
-
for (const backup of hookBackups) {
|
|
273
|
-
const hookDir = path.join(outputPath, backup.relativePath);
|
|
274
|
-
fs.writeFileSync(path.join(hookDir, backup.name), backup.content);
|
|
275
|
-
}
|
|
276
290
|
}
|
|
277
|
-
|
|
278
|
-
processSourceHooks(generatorOptions.outputPath);
|
|
279
|
-
|
|
280
|
-
console.log(
|
|
281
|
-
'[INFO] [generateCode] STATUS=FINISHED | Code generation complete.'
|
|
282
|
-
);
|
|
283
291
|
}
|
|
292
|
+
|
package/src/generateIntTest.js
CHANGED
|
@@ -105,6 +105,17 @@ async function writeSource({ templatePath, templateData, setting }, { checkCreat
|
|
|
105
105
|
return [true, {}];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Result-Driven generator to generate Integration Tests.
|
|
110
|
+
* Does not have createSource/libs dependencies.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} rootPath - The root path of the project.
|
|
113
|
+
* @param {object} [options={}] - Configuration options.
|
|
114
|
+
* @param {string} [options.outputPath] - Output directory path.
|
|
115
|
+
* @param {string|null} [options.s3Prefix=null] - S3 key prefix for integration test resources.
|
|
116
|
+
* @param {object} [options._izContext] - Integration context containing logger interface.
|
|
117
|
+
* @returns {Promise<{serviceTag: string, count: number, durationMs: number}>}
|
|
118
|
+
*/
|
|
108
119
|
export async function generateIntTest(rootPath, options = {}) {
|
|
109
120
|
const {
|
|
110
121
|
outputPath = rootPath,
|
|
@@ -45,6 +45,15 @@ function writeSource({ templatePath, templateData, setting }, { checkCreateSourc
|
|
|
45
45
|
return [true, {}];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Result-Driven generator to generate Integration Test resources.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} rootPath - The root path of the project.
|
|
52
|
+
* @param {object} [options={}] - Configuration options.
|
|
53
|
+
* @param {string} [options.outputPath] - Output directory path.
|
|
54
|
+
* @param {object} [options._izContext] - Integration context containing logger interface.
|
|
55
|
+
* @returns {Promise<{serviceTag: string, count: number, durationMs: number}>}
|
|
56
|
+
*/
|
|
48
57
|
export async function generateResourceIntTest(rootPath, options = {}) {
|
|
49
58
|
const {
|
|
50
59
|
outputPath = rootPath,
|
|
@@ -44,7 +44,7 @@ async function generateCodeWithTemplate(_izContext, savePath, settings) {
|
|
|
44
44
|
let allIntTestConfig = await getIntTestConfig(
|
|
45
45
|
_izContext,
|
|
46
46
|
'generateIntegrationTest/libs/',
|
|
47
|
-
rootPath
|
|
47
|
+
rootPath || savePath
|
|
48
48
|
);
|
|
49
49
|
const event = await generateEvent(_izContext, allIntTestConfig, savePath, serviceTag);
|
|
50
50
|
const test = await generateTest(_izContext, allIntTestConfig, savePath, serviceTag);
|
|
@@ -393,10 +393,18 @@ function createResourceName(eventTag) {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
async function getIntTestConfig(_izContext, intTestPath, rootPath) {
|
|
396
|
+
// ponytail: find first existing integration test libs folder or fall back to standard path
|
|
397
|
+
const targetLibsPath = rootPath.includes('generateIntegrationTest')
|
|
398
|
+
? path.join(rootPath.split('generateIntegrationTest')[0], 'generateIntegrationTest', 'libs')
|
|
399
|
+
: [
|
|
400
|
+
path.join(rootPath, 'app/src/generateIntegrationTest/libs'),
|
|
401
|
+
path.join(rootPath, 'src/generateIntegrationTest/libs')
|
|
402
|
+
].find(fs.existsSync) || path.join(rootPath, 'src/generateIntegrationTest/libs');
|
|
403
|
+
|
|
396
404
|
return await getObjectSchema.getDataFromPath(
|
|
397
405
|
_izContext,
|
|
398
|
-
|
|
399
|
-
path.
|
|
406
|
+
path.basename(targetLibsPath),
|
|
407
|
+
path.dirname(targetLibsPath),
|
|
400
408
|
{ exceptFileName: 'saveConfig' }
|
|
401
409
|
);
|
|
402
410
|
}
|
package/src/generateSchema.js
CHANGED
|
@@ -15,34 +15,36 @@ import { generatePropertyValueRelationshipSchemas } from './schemaGenerators/app
|
|
|
15
15
|
import { generateAttributeTreeObjectSchemas } from './schemaGenerators/app/src/schemas/ObjectSchemas/AttributeTreeSchemaGenerator.js';
|
|
16
16
|
import { generateAttributeTreeRelationshipSchemas } from './schemaGenerators/app/src/schemas/RelationshipSchemas/AttributeTreeRelationshipSchemaGenerator.js';
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Low-level generator to generate dynamic schemas (FlowSchema, etc.).
|
|
20
|
+
*
|
|
21
|
+
* @param {string} rootPath - The root path of the project.
|
|
22
|
+
* @param {object} [options={}] - Configuration options.
|
|
23
|
+
* @param {string} [options.configPath] - Path to the serverless configuration file.
|
|
24
|
+
* @param {string} [options.outputPath] - Output directory path.
|
|
25
|
+
* @param {Array} [options.schemaTemplateConfig] - Schema template configuration.
|
|
26
|
+
* @returns {Promise<void>}
|
|
27
|
+
*/
|
|
28
|
+
export async function generateSchema(rootPath, options = {}) {
|
|
19
29
|
console.log(
|
|
20
30
|
'[INFO] [generateSchema] STATUS=STARTING | Generating dynamic schemas (FlowSchema, etc.)...'
|
|
21
31
|
);
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const serviceConfigStr = fs.readFileSync(configPath, 'utf8');
|
|
28
|
-
const serviceConfig = yaml.parse(serviceConfigStr);
|
|
29
|
-
serviceTag = serviceConfig?.main_config?.iz_serviceTag || 'unknownService';
|
|
30
|
-
} catch (e) {
|
|
31
|
-
console.warn(
|
|
32
|
-
`[WARNING] Could not read service config at ${configPath}. Using default serviceTag.`
|
|
33
|
-
);
|
|
34
|
-
}
|
|
33
|
+
const configPath =
|
|
34
|
+
options.configPath || path.join(rootPath, 'config', 'serverless.config.yml');
|
|
35
|
+
const serviceTag = readServiceTag(configPath);
|
|
35
36
|
|
|
36
|
-
// Phase 1 parsing just to get the raw data needed for generation
|
|
37
37
|
const objectData = await parseObjectSchemas(rootPath);
|
|
38
|
+
applySchemaTemplateConfig(objectData, options.schemaTemplateConfig, serviceTag);
|
|
39
|
+
|
|
38
40
|
const flowData = await parseFlowSchemas(rootPath, objectData);
|
|
39
41
|
const relationshipData = await parseRelationshipSchemas(rootPath, objectData);
|
|
40
42
|
|
|
41
|
-
// Call Schema Generators (Phase 1)
|
|
42
43
|
const generatorOptions = {
|
|
43
44
|
outputPath: options.outputPath || rootPath,
|
|
44
45
|
serviceTag
|
|
45
46
|
};
|
|
47
|
+
|
|
46
48
|
await generateSystemTextSchemas(
|
|
47
49
|
objectData,
|
|
48
50
|
relationshipData,
|
|
@@ -56,8 +58,66 @@ export async function generateSchema(rootPath, options) {
|
|
|
56
58
|
await generatePropertyValueRelationshipSchemas(objectData, generatorOptions);
|
|
57
59
|
await generateAttributeTreeObjectSchemas(objectData, generatorOptions);
|
|
58
60
|
await generateAttributeTreeRelationshipSchemas(objectData, generatorOptions);
|
|
61
|
+
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
function readServiceTag(configPath) {
|
|
64
|
+
try {
|
|
65
|
+
const serviceConfigStr = fs.readFileSync(configPath, 'utf8');
|
|
66
|
+
const serviceConfig = yaml.parse(serviceConfigStr);
|
|
67
|
+
return serviceConfig?.main_config?.iz_serviceTag || 'unknownService';
|
|
68
|
+
} catch {
|
|
69
|
+
console.warn(
|
|
70
|
+
`[WARNING] Could not read service config at ${configPath}. Using default serviceTag.`
|
|
71
|
+
);
|
|
72
|
+
return 'unknownService';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function applySchemaTemplateConfig(objectData, schemaTemplateConfig, defaultServiceTag) {
|
|
77
|
+
const templates = Array.isArray(schemaTemplateConfig)
|
|
78
|
+
? schemaTemplateConfig
|
|
79
|
+
: [];
|
|
80
|
+
|
|
81
|
+
for (const template of templates) {
|
|
82
|
+
if (template?.generateName !== 'mainAttributeTree') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const objectType = template?.objType?.objectType;
|
|
87
|
+
const serviceTag = template?.objType?.serviceTag || defaultServiceTag;
|
|
88
|
+
const attributeTag = template?.attributeTag;
|
|
89
|
+
|
|
90
|
+
if (!objectType || !attributeTag) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const objectSchema = objectData.objects.find(
|
|
95
|
+
obj =>
|
|
96
|
+
obj.objectType === objectType &&
|
|
97
|
+
(obj.serviceTag || defaultServiceTag) === serviceTag
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!objectSchema) {
|
|
101
|
+
console.warn(
|
|
102
|
+
`[WARNING] [generateSchema] Template target not found for ${serviceTag}.${objectType}`
|
|
103
|
+
);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
objectSchema.addOnDataStructure ||= [];
|
|
108
|
+
|
|
109
|
+
const hasAttributeTree = objectSchema.addOnDataStructure.some(
|
|
110
|
+
addOn =>
|
|
111
|
+
addOn?.type === 'attributeTree' &&
|
|
112
|
+
addOn?.attributeTreeTag === attributeTag
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!hasAttributeTree) {
|
|
116
|
+
// ponytail: inject the same addon shape the existing generators already understand
|
|
117
|
+
objectSchema.addOnDataStructure.push({
|
|
118
|
+
type: 'attributeTree',
|
|
119
|
+
attributeTreeTag: attributeTag
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
63
123
|
}
|