@izara_project/izara-core-generate-service-code 1.0.99 → 1.0.101
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 +3 -1
- package/src/compile/loadSchemas.js +18 -4
- package/src/compile/schemaValidatorSetup.js +16 -0
- package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +108 -108
- 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/CreateLogicalLib.ejs +20 -7
- package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
- 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/custom/customFlowPaths.js +13 -16
- package/src/emit/application/custom/customFlowS3.js +456 -63
- package/src/emit/application/custom/customFlows.js +131 -0
- package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +7 -4
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +111 -0
- package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +100 -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 +240 -27
- package/src/emit/application/custom/templates/s3/Start_Main.ejs +216 -0
- package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +5 -1
- package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +67 -60
- package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +27 -9
- 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 +3 -8
- package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
- package/src/emit/infrastructure/resources/iamResourceArtifacts.js +22 -0
- package/src/emit/infrastructure/resources/resourceState.js +15 -5
- package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -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/error/emitErrorSchemas.js +120 -10
- package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
- package/src/generate.js +32 -30
- package/src/plan/domainRegistry.js +2 -1
- package/test/integration/publicApi.test.js +33 -24
- package/test/unit/application/createTargetRoleHandler.test.js +33 -10
- package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
- package/test/unit/application/customFlowS3.test.js +858 -0
- package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
- package/test/unit/infrastructure/resourceArtifacts.test.js +51 -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
|
@@ -83,14 +83,16 @@ test('filters all SQS functions by their calling flow', () => {
|
|
|
83
83
|
/#<#CreateCompleteHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateCompleteHdrSqs/
|
|
84
84
|
);
|
|
85
85
|
assert.match(completion, /#<#\/CreateCompleteHdrSqsFilterPatterns#>/);
|
|
86
|
-
assert.match(completion, /callingFlow:\s*\n\s
|
|
87
|
-
assert.
|
|
86
|
+
assert.match(completion, /callingFlow:\s*\n\s*StringValue:/);
|
|
87
|
+
assert.doesNotMatch(completion, /callingFlow:\s*\n\s*- exists: false/);
|
|
88
|
+
assert.doesNotMatch(completion, /MessageAttributes:\s*\n\s*- exists: false/);
|
|
88
89
|
assert.match(
|
|
89
90
|
ordinary,
|
|
90
91
|
/#<#CreateHdrSqsFilterPatterns#>[\s\S]*filterPatterns:[\s\S]*\$\{self:custom\.iz_resourcePrefix\}CreateHdrSqs/
|
|
91
92
|
);
|
|
92
93
|
assert.match(ordinary, /#<#\/CreateHdrSqsFilterPatterns#>/);
|
|
93
|
-
assert.match(ordinary, /callingFlow:\s*\n\s
|
|
94
|
-
assert.
|
|
94
|
+
assert.match(ordinary, /callingFlow:\s*\n\s*StringValue:/);
|
|
95
|
+
assert.doesNotMatch(ordinary, /callingFlow:\s*\n\s*- exists: false/);
|
|
96
|
+
assert.doesNotMatch(ordinary, /MessageAttributes:\s*\n\s*- exists: false/);
|
|
95
97
|
});
|
|
96
98
|
|
|
@@ -7,6 +7,10 @@ import test, { after } from 'node:test';
|
|
|
7
7
|
process.env.NODE_ENV = 'test';
|
|
8
8
|
|
|
9
9
|
import { generateResourceArtifacts } from '../../../src/emit/infrastructure/resources/generateResourceArtifacts.js';
|
|
10
|
+
import {
|
|
11
|
+
renderS3,
|
|
12
|
+
toAlphanumericPascalCase
|
|
13
|
+
} from '../../../src/emit/infrastructure/resources/s3ResourceArtifacts.js';
|
|
10
14
|
|
|
11
15
|
const roots = new Set();
|
|
12
16
|
after(() =>
|
|
@@ -909,3 +913,50 @@ async function createProject() {
|
|
|
909
913
|
);
|
|
910
914
|
return rootPath;
|
|
911
915
|
}
|
|
916
|
+
|
|
917
|
+
test('toAlphanumericPascalCase converts non-alphanumeric strings to PascalCase', () => {
|
|
918
|
+
assert.equal(
|
|
919
|
+
toAlphanumericPascalCase('video-record-temp-test'),
|
|
920
|
+
'VideoRecordTempTest'
|
|
921
|
+
);
|
|
922
|
+
assert.equal(
|
|
923
|
+
toAlphanumericPascalCase('video.record_temp-test'),
|
|
924
|
+
'VideoRecordTempTest'
|
|
925
|
+
);
|
|
926
|
+
assert.equal(toAlphanumericPascalCase('AlreadyPascal'), 'AlreadyPascal');
|
|
927
|
+
assert.equal(toAlphanumericPascalCase(''), '');
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
test('renderS3 generates alphanumeric PascalCase Logical ID for CloudFormation', async () => {
|
|
931
|
+
const yaml = await renderS3({
|
|
932
|
+
s3Buckets: ['video-record-temp-test']
|
|
933
|
+
});
|
|
934
|
+
assert.ok(
|
|
935
|
+
yaml.includes('VideoRecordTempTestS3Bucket:'),
|
|
936
|
+
'Logical ID must be alphanumeric PascalCase without hyphens'
|
|
937
|
+
);
|
|
938
|
+
assert.ok(
|
|
939
|
+
!yaml.includes('video-record-temp-testS3Bucket:'),
|
|
940
|
+
'Logical ID must not contain hyphens'
|
|
941
|
+
);
|
|
942
|
+
assert.ok(
|
|
943
|
+
yaml.includes('BucketName: video-record-temp-test'),
|
|
944
|
+
'BucketName property must preserve original bucket name'
|
|
945
|
+
);
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
test('roleStatements emits s3:GetObject for custom S3 flow bucket', async () => {
|
|
949
|
+
const rootPath = await createProject();
|
|
950
|
+
await generateResourceArtifacts(rootPath);
|
|
951
|
+
const iam = await readFile(
|
|
952
|
+
join(
|
|
953
|
+
rootPath,
|
|
954
|
+
'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'
|
|
955
|
+
),
|
|
956
|
+
'utf8'
|
|
957
|
+
);
|
|
958
|
+
const uploadRole = roleBlock(iam, 'UploadRole');
|
|
959
|
+
assert.match(uploadRole, /s3:GetObject/);
|
|
960
|
+
assert.match(uploadRole, /arn:aws:s3:::UploadBucket\/\*/);
|
|
961
|
+
});
|
|
962
|
+
|
|
@@ -26,7 +26,8 @@ const STATIC_RBAC_ACTIONS = [
|
|
|
26
26
|
const STATIC_LIMIT_ACTIONS = [
|
|
27
27
|
'ReservedDynamicUsage',
|
|
28
28
|
'ConfirmUsage',
|
|
29
|
-
'CancelUsage'
|
|
29
|
+
'CancelUsage',
|
|
30
|
+
'ReservedUsageTtlExpired'
|
|
30
31
|
];
|
|
31
32
|
|
|
32
33
|
after(async () => {
|
|
@@ -113,11 +114,12 @@ test('generates shared and object-specific RBAC flow schemas', async () => {
|
|
|
113
114
|
'lambdaSyncInv',
|
|
114
115
|
'lambdaSyncApi'
|
|
115
116
|
]);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
|
|
117
|
+
const reservedUsageTtlExpired = await readGeneratedFlow(
|
|
118
|
+
rootPath,
|
|
119
|
+
'ReservedUsageTtlExpired',
|
|
120
|
+
'FlowLimit'
|
|
121
|
+
);
|
|
122
|
+
assert.deepEqual(reservedUsageTtlExpired.event, ['dss']);
|
|
121
123
|
});
|
|
122
124
|
|
|
123
125
|
test('removes RBAC schemas when the last RBAC object is removed', async () => {
|
|
@@ -6,6 +6,7 @@ import test, { after } from 'node:test';
|
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
|
|
8
8
|
import { generateDynamicRelationshipFlowSchemas } from '../../../src/emit/application/relationship/generateDynamicRelationshipFlowSchemas.js';
|
|
9
|
+
import { loadLocalSchemaDirectory } from '../../../src/compile/loadSchemas.js';
|
|
9
10
|
|
|
10
11
|
const RELATIONSHIP_ACTIONS = [
|
|
11
12
|
'CreateRelationship',
|
|
@@ -136,6 +137,56 @@ test('does not allow options to override the rootPath argument', async () => {
|
|
|
136
137
|
);
|
|
137
138
|
});
|
|
138
139
|
|
|
140
|
+
test('validates relationship schemas with systemNoUser and systemWithUser generatedBy', async () => {
|
|
141
|
+
const rootPath = await mkdtemp(
|
|
142
|
+
join(tmpdir(), 'generate-service-code-v2-rel-val-')
|
|
143
|
+
);
|
|
144
|
+
projectRoots.add(rootPath);
|
|
145
|
+
const relationshipDirectory = join(
|
|
146
|
+
rootPath,
|
|
147
|
+
'app/src/schemas/RelationshipSchemas'
|
|
148
|
+
);
|
|
149
|
+
await mkdir(relationshipDirectory, { recursive: true });
|
|
150
|
+
const relSchema = [
|
|
151
|
+
{
|
|
152
|
+
isItemLabel: {
|
|
153
|
+
fieldNames: {},
|
|
154
|
+
storageResources: {
|
|
155
|
+
myGraph: { storageType: 'graph', graphServerTag: 'GraphHandler' }
|
|
156
|
+
},
|
|
157
|
+
generatedBy: 'systemNoUser',
|
|
158
|
+
links: [
|
|
159
|
+
{
|
|
160
|
+
storageResourceTags: ['myGraph'],
|
|
161
|
+
from: {
|
|
162
|
+
objType: { serviceTag: 'ItemSvc', objectType: 'itemValue' },
|
|
163
|
+
linkType: 'many',
|
|
164
|
+
requiredOnCreate: true
|
|
165
|
+
},
|
|
166
|
+
to: {
|
|
167
|
+
objType: { serviceTag: 'ItemSvc', objectType: 'itemLabel' },
|
|
168
|
+
linkType: 'one'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
await writeFile(
|
|
176
|
+
join(relationshipDirectory, 'isItemLabel.js'),
|
|
177
|
+
`export default ${JSON.stringify(relSchema, null, 2)};\n`
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const schemas = await loadLocalSchemaDirectory(
|
|
181
|
+
{ schemaRoot: join(rootPath, 'app/src/schemas') },
|
|
182
|
+
'RelationshipSchemas',
|
|
183
|
+
{ validateSchemas: true }
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
assert.equal(schemas.length, 1);
|
|
187
|
+
assert.equal(schemas[0].isItemLabel.generatedBy, 'systemNoUser');
|
|
188
|
+
});
|
|
189
|
+
|
|
139
190
|
async function createProject({ singletonExport = false } = {}) {
|
|
140
191
|
const rootPath = await mkdtemp(
|
|
141
192
|
join(tmpdir(), 'generate-service-code-v2-relationship-')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import { readFile, rm,
|
|
2
|
+
import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import test, { after } from 'node:test';
|
|
@@ -7,7 +7,10 @@ import test, { after } from 'node:test';
|
|
|
7
7
|
import {
|
|
8
8
|
emitErrorSchemas,
|
|
9
9
|
errorSchemaManagedPaths,
|
|
10
|
-
ERROR_SCHEMA_PATH
|
|
10
|
+
ERROR_SCHEMA_PATH,
|
|
11
|
+
SHARED_ERROR_PACKAGE_PATH,
|
|
12
|
+
SHARED_ERROR_INDEX_PATH,
|
|
13
|
+
SHARED_ERROR_CONSTS_PATH
|
|
11
14
|
} from '../../../src/emit/schemas/error/emitErrorSchemas.js';
|
|
12
15
|
import { generate } from '../../../src/generate.js';
|
|
13
16
|
|
|
@@ -49,7 +52,7 @@ test('emitErrorSchemas produces expected ERROR_TYPES for graph and general error
|
|
|
49
52
|
]
|
|
50
53
|
});
|
|
51
54
|
|
|
52
|
-
assert.equal(artifacts.length,
|
|
55
|
+
assert.equal(artifacts.length, 4);
|
|
53
56
|
assert.equal(artifacts[0].path, ERROR_SCHEMA_PATH);
|
|
54
57
|
|
|
55
58
|
const content = artifacts[0].content;
|
|
@@ -86,6 +89,31 @@ test('emitErrorSchemas produces expected ERROR_TYPES for graph and general error
|
|
|
86
89
|
assert.ok(customIdx < explicitIdx);
|
|
87
90
|
assert.ok(explicitIdx < invalidIdx);
|
|
88
91
|
assert.ok(invalidIdx < notFoundIdx);
|
|
92
|
+
|
|
93
|
+
// Shared library artifacts
|
|
94
|
+
assert.equal(artifacts[1].path, SHARED_ERROR_PACKAGE_PATH);
|
|
95
|
+
const parsedPkg = JSON.parse(artifacts[1].content);
|
|
96
|
+
assert.equal(
|
|
97
|
+
parsedPkg.name,
|
|
98
|
+
'@izara_project/izara-myservice-library-errorlib'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
assert.equal(artifacts[2].path, SHARED_ERROR_INDEX_PATH);
|
|
102
|
+
assert.match(
|
|
103
|
+
artifacts[2].content,
|
|
104
|
+
/import \* as errorLib from '\.\/src\/errorConsts\.js';/
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
assert.equal(artifacts[3].path, SHARED_ERROR_CONSTS_PATH);
|
|
108
|
+
const sharedContent = artifacts[3].content;
|
|
109
|
+
assert.match(
|
|
110
|
+
sharedContent,
|
|
111
|
+
/\/\/ only generate error that sharedLibrary: true/
|
|
112
|
+
);
|
|
113
|
+
assert.match(sharedContent, /INVALID_GENERATED_BY:/);
|
|
114
|
+
assert.match(sharedContent, /NOT_FOUND_SCHEMA:/);
|
|
115
|
+
assert.doesNotMatch(sharedContent, /CUSTOM_INTERNAL_ERROR:/);
|
|
116
|
+
assert.doesNotMatch(sharedContent, /EXPLICIT_SERVICE_ERROR:/);
|
|
89
117
|
});
|
|
90
118
|
|
|
91
119
|
test('emitErrorSchemas returns empty array when no errors provided', async () => {
|
|
@@ -97,9 +125,18 @@ test('emitErrorSchemas returns empty array when no errors provided', async () =>
|
|
|
97
125
|
assert.deepEqual(artifacts, []);
|
|
98
126
|
assert.deepEqual(errorSchemaManagedPaths([]), []);
|
|
99
127
|
assert.deepEqual(errorSchemaManagedPaths(['someError']), [ERROR_SCHEMA_PATH]);
|
|
128
|
+
assert.deepEqual(
|
|
129
|
+
errorSchemaManagedPaths([{ errorTag: 'E1', sharedLibrary: true }]),
|
|
130
|
+
[
|
|
131
|
+
ERROR_SCHEMA_PATH,
|
|
132
|
+
SHARED_ERROR_PACKAGE_PATH,
|
|
133
|
+
SHARED_ERROR_INDEX_PATH,
|
|
134
|
+
SHARED_ERROR_CONSTS_PATH
|
|
135
|
+
]
|
|
136
|
+
);
|
|
100
137
|
});
|
|
101
138
|
|
|
102
|
-
test('generate creates
|
|
139
|
+
test('generate creates errorConsts.js and libs/errorLib when ErrorSchemas directory exists', async () => {
|
|
103
140
|
const rootPath = join(
|
|
104
141
|
tmpdir(),
|
|
105
142
|
`error-schema-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
@@ -141,7 +178,7 @@ test('generate creates errorSchema.js when ErrorSchemas directory exists', async
|
|
|
141
178
|
|
|
142
179
|
const targetFile = join(
|
|
143
180
|
rootPath,
|
|
144
|
-
'app/src/schemas/ErrorSchemas/generateCode/
|
|
181
|
+
'app/src/schemas/ErrorSchemas/generateCode/errorConsts.js'
|
|
145
182
|
);
|
|
146
183
|
const content = await readFile(targetFile, 'utf8');
|
|
147
184
|
|
|
@@ -151,4 +188,46 @@ test('generate creates errorSchema.js when ErrorSchemas directory exists', async
|
|
|
151
188
|
assert.match(content, /MISSING_USER_NODE:/);
|
|
152
189
|
assert.match(content, /serviceTag:\s*'CategoryTreeStandard'/);
|
|
153
190
|
assert.doesNotMatch(content, /export default/);
|
|
191
|
+
|
|
192
|
+
// Verify libs/errorLib files
|
|
193
|
+
const pkgFile = join(rootPath, 'libs/errorLib/package.json');
|
|
194
|
+
const pkgContent = JSON.parse(await readFile(pkgFile, 'utf8'));
|
|
195
|
+
assert.equal(
|
|
196
|
+
pkgContent.name,
|
|
197
|
+
'@izara_project/izara-categorytreestandard-library-errorlib'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const indexFile = join(rootPath, 'libs/errorLib/index.js');
|
|
201
|
+
const indexContent = await readFile(indexFile, 'utf8');
|
|
202
|
+
assert.match(indexContent, /export { errorLib };/);
|
|
203
|
+
|
|
204
|
+
const sharedConstsFile = join(rootPath, 'libs/errorLib/src/errorConsts.js');
|
|
205
|
+
const sharedConstsContent = await readFile(sharedConstsFile, 'utf8');
|
|
206
|
+
assert.match(
|
|
207
|
+
sharedConstsContent,
|
|
208
|
+
/\/\/ only generate error that sharedLibrary: true/
|
|
209
|
+
);
|
|
210
|
+
assert.match(sharedConstsContent, /NOT_FOUND_SCHEMA:/);
|
|
211
|
+
assert.match(sharedConstsContent, /INVALID_GENERATED_BY:/);
|
|
212
|
+
assert.match(sharedConstsContent, /MISSING_USER_NODE:/);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('generate creates ErrorSchemas directory if it does not exist', async () => {
|
|
216
|
+
const rootPath = join(
|
|
217
|
+
tmpdir(),
|
|
218
|
+
`error-schema-dir-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
219
|
+
);
|
|
220
|
+
roots.add(rootPath);
|
|
221
|
+
await mkdir(join(rootPath, 'config'), { recursive: true });
|
|
222
|
+
|
|
223
|
+
await writeFile(
|
|
224
|
+
join(rootPath, 'config/serverless.config.yml'),
|
|
225
|
+
'main_config:\n iz_serviceTag: TestService\n'
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
await generate(rootPath);
|
|
229
|
+
|
|
230
|
+
const errorSchemasDir = join(rootPath, 'app/src/schemas/ErrorSchemas');
|
|
231
|
+
const dirStat = await stat(errorSchemasDir);
|
|
232
|
+
assert.ok(dirStat.isDirectory());
|
|
154
233
|
});
|