@izara_project/izara-core-generate-service-code 1.0.81 → 1.0.83
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/flowValidator.js +23 -0
- package/src/compile/loadSchemas.js +5 -0
- package/src/emit/crud/crudActionPlan.js +7 -4
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +1 -1
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +28 -5
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +5 -2
- package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +2 -2
- package/src/emit/crud/templates/FlowMain_Wbs.ejs +6 -2
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +1 -1
- package/src/emit/custom/customFlows.js +2 -0
- package/src/emit/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -2
- package/src/emit/custom/templates/s3/CreatePreSignUrl_Main.ejs +2 -1
- package/src/emit/custom/templates/s3/ReservedLimit_Main.ejs +1 -1
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +2 -2
- package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
- package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +9 -2
- package/src/emit/resource/emitResourceArtifacts.js +9 -5
- package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
- package/src/emit/system/templates/RegisterComplete_Main.ejs +3 -3
- package/src/emit/system/templates/Register_HdrWbs.ejs +2 -2
- package/src/emit/system/templates/Register_Main.ejs +4 -4
- package/src/emit/system/templates/WebSocketComplete_Main.ejs +1 -1
- package/src/emit/systemText/emitSystemTextSchemas.js +10 -9
- package/test/crudApplicationArtifacts.test.js +21 -10
- package/test/customFlowApplicationArtifacts.test.js +8 -0
- package/test/dynamicCrudFlowSchemas.test.js +21 -11
- package/test/publicApi.test.js +3 -2
- package/test/resourceArtifacts.test.js +2 -2
- package/test/systemTextSchemas.test.js +9 -9
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.83",
|
|
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": {
|
|
@@ -3,6 +3,29 @@ export function validateFlowSchema(flowSchema) {
|
|
|
3
3
|
throw new TypeError('flowSchema must be an object');
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
if (flowSchema.initiateBy !== undefined) {
|
|
7
|
+
const validInitiateBy = [
|
|
8
|
+
'user',
|
|
9
|
+
'systemWithNoUser',
|
|
10
|
+
'systemWithUser',
|
|
11
|
+
'system'
|
|
12
|
+
];
|
|
13
|
+
if (!validInitiateBy.includes(flowSchema.initiateBy)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`FlowSchema validation error: initiateBy must be one of ${JSON.stringify(validInitiateBy)}, got '${flowSchema.initiateBy}'`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (flowSchema.handleObj !== undefined) {
|
|
21
|
+
const validHandleObj = ['one', 'multi'];
|
|
22
|
+
if (!validHandleObj.includes(flowSchema.handleObj)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`FlowSchema validation error: handleObj must be one of ${JSON.stringify(validHandleObj)}, got '${flowSchema.handleObj}'`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
const steps = flowSchema.flowSteps;
|
|
7
30
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
8
31
|
throw new Error(
|
|
@@ -42,6 +42,11 @@ export async function loadLocalSchemaDirectory(project, directoryName) {
|
|
|
42
42
|
if (Array.isArray(module.default)) {
|
|
43
43
|
loadedSchemas = module.default;
|
|
44
44
|
}
|
|
45
|
+
loadedSchemas = loadedSchemas.filter(
|
|
46
|
+
(schema) =>
|
|
47
|
+
schema && typeof schema === 'object' && Object.keys(schema).length > 0
|
|
48
|
+
);
|
|
49
|
+
if (loadedSchemas.length === 0) continue;
|
|
45
50
|
|
|
46
51
|
if (directoryName === 'FlowSchemas') {
|
|
47
52
|
for (const schema of loadedSchemas) {
|
|
@@ -31,11 +31,14 @@ export function createCrudActionPlan(
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
const VALID_CRUD_GENERATED_BY = new Set([
|
|
35
|
+
'user',
|
|
36
|
+
'systemWithUser',
|
|
37
|
+
'systemWithNoUser'
|
|
38
|
+
]);
|
|
39
|
+
|
|
34
40
|
export function isCrudObject(object) {
|
|
35
|
-
return (
|
|
36
|
-
object?.generatedBy === 'userGenerated' ||
|
|
37
|
-
object?.generatedBy === 'systemGenerated'
|
|
38
|
-
);
|
|
41
|
+
return VALID_CRUD_GENERATED_BY.has(object?.generatedBy);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
export function hasGraphStorageResource(objects = []) {
|
|
@@ -103,7 +103,7 @@ function _createDynamicFlow(action, plan, serviceTag = '') {
|
|
|
103
103
|
|
|
104
104
|
return {
|
|
105
105
|
flowTag: action,
|
|
106
|
-
initiateBy: '
|
|
106
|
+
initiateBy: 'systemWithNoUser',
|
|
107
107
|
handleObj: 'one',
|
|
108
108
|
statusType: 'none',
|
|
109
109
|
event: plan.outputTopic ? ['ownTopic'] : ['lambdaSyncInv', 'lambdaSyncApi'],
|
|
@@ -82,7 +82,10 @@ export default async function <%- flowTag %>(
|
|
|
82
82
|
|
|
83
83
|
// ── Resolve userId / targetId ─────────────────────────────────────────────
|
|
84
84
|
let userId, targetId;
|
|
85
|
-
if (
|
|
85
|
+
if (
|
|
86
|
+
objectSchema.generatedBy === 'user' ||
|
|
87
|
+
objectSchema.generatedBy === 'systemWithUser'
|
|
88
|
+
) {
|
|
86
89
|
userId = _izContext.correlationIds.get(coreConsts.BASE_USER_ID);
|
|
87
90
|
targetId = _izContext.correlationIds.get(coreConsts.TARGET_ID);
|
|
88
91
|
if (!userId) errorsFound.push('Not have userId');
|
|
@@ -126,14 +129,34 @@ export default async function <%- flowTag %>(
|
|
|
126
129
|
);
|
|
127
130
|
_izContext.logger.debug('errorsFound', errorsFound);
|
|
128
131
|
|
|
129
|
-
// ──
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
// ── Resolve fromRequiredOnCreate fields ──────────────────────────────────
|
|
133
|
+
if (objectSchemas.fieldNames) {
|
|
134
|
+
if (!requestParams.fieldNames) requestParams.fieldNames = {};
|
|
135
|
+
for (const [fieldName, val] of Object.entries(objectSchemas.fieldNames)) {
|
|
136
|
+
if (val.fromRequiredOnCreate && requestParams.fieldNames[fieldName] === undefined) {
|
|
137
|
+
const fromConfig = val.fromRequiredOnCreate;
|
|
138
|
+
const sourceField = fromConfig.fieldName || fieldName;
|
|
139
|
+
if (fromConfig.belongToObjType === true && targetId) {
|
|
140
|
+
requestParams.fieldNames[fieldName] = targetId;
|
|
141
|
+
} else if (fromConfig.relType && requestParams.relationships?.length > 0) {
|
|
142
|
+
const matchedRel = requestParams.relationships.find(
|
|
143
|
+
(rel) =>
|
|
144
|
+
rel.relType?.serviceTag === fromConfig.relType.serviceTag &&
|
|
145
|
+
rel.relType?.relationshipTag === fromConfig.relType.relationshipTag
|
|
146
|
+
);
|
|
147
|
+
if (matchedRel?.targetIdentifiers?.[sourceField] !== undefined) {
|
|
148
|
+
requestParams.fieldNames[fieldName] = matchedRel.targetIdentifiers[sourceField];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
132
154
|
|
|
133
155
|
for (const [key, val] of Object.entries(objectSchemas.fieldNames)) {
|
|
134
156
|
if (
|
|
135
157
|
val.randomOnCreate === true ||
|
|
136
158
|
val.requiredOnCreate === true ||
|
|
159
|
+
val.fromRequiredOnCreate ||
|
|
137
160
|
val.hashOnCreate?.length
|
|
138
161
|
) {
|
|
139
162
|
listOfRequiredOnCreate.push(key);
|
|
@@ -222,7 +245,7 @@ export default async function <%- flowTag %>(
|
|
|
222
245
|
}
|
|
223
246
|
|
|
224
247
|
<%_
|
|
225
|
-
const rbacObjects = objects.filter(o => (o
|
|
248
|
+
const rbacObjects = objects.filter(o => isCrudObject(o) && hasRbacAddOn(o));
|
|
226
249
|
const rbacObjectTypes = rbacObjects.map(o => o.objectType);
|
|
227
250
|
const rbacServiceTagMap = {};
|
|
228
251
|
for (const o of rbacObjects) {
|
|
@@ -75,7 +75,10 @@ export default async function <%- flowTag %>(
|
|
|
75
75
|
_izContext.logger.debug('objectSchema', objectSchema);
|
|
76
76
|
|
|
77
77
|
// ── Resolve userId / targetId ─────────────────────────────────────────────
|
|
78
|
-
if (
|
|
78
|
+
if (
|
|
79
|
+
objectSchema.generatedBy === 'user' ||
|
|
80
|
+
objectSchema.generatedBy === 'systemWithUser'
|
|
81
|
+
) {
|
|
79
82
|
const userId = _izContext.correlationIds.get(coreConsts.BASE_USER_ID);
|
|
80
83
|
const targetId = _izContext.correlationIds.get(coreConsts.TARGET_ID);
|
|
81
84
|
if (!userId) errorsFound.push('Not have userId');
|
|
@@ -98,7 +101,7 @@ export default async function <%- flowTag %>(
|
|
|
98
101
|
);
|
|
99
102
|
|
|
100
103
|
<%_
|
|
101
|
-
const rbacObjects = objects.filter(o => (o
|
|
104
|
+
const rbacObjects = objects.filter(o => isCrudObject(o) && hasRbacAddOn(o));
|
|
102
105
|
const rbacObjectTypes = rbacObjects.map(o => o.objectType);
|
|
103
106
|
const rbacServiceTagMap = {};
|
|
104
107
|
for (const o of rbacObjects) {
|
|
@@ -116,7 +116,7 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
116
116
|
} else {
|
|
117
117
|
const connId = event.connId;
|
|
118
118
|
// await postMessage({ message: event.message }, connId);
|
|
119
|
-
await postToConnection({ message: event.message }, connId);
|
|
119
|
+
await postToConnection(event._izContext, { message: event.message }, connId);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
return response.webSocketSuccess();
|
|
@@ -124,7 +124,7 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
124
124
|
const connId = event.requestContext.connectionId;
|
|
125
125
|
event._izContext.logger.error('Error, WebSocket: ', err);
|
|
126
126
|
// await sendErrorMessage({ message: err.message }, connId);
|
|
127
|
-
await postToConnection({ message: err.message }, connId);
|
|
127
|
+
await postToConnection(event._izContext, { message: err.message }, connId);
|
|
128
128
|
return response.failure(err);
|
|
129
129
|
}
|
|
130
130
|
});
|
|
@@ -70,7 +70,10 @@ export default async function <%- typeof functionName !== 'undefined' ? function
|
|
|
70
70
|
|
|
71
71
|
_izContext.logger.debug('flowSchema', flowSchema);
|
|
72
72
|
|
|
73
|
-
if (
|
|
73
|
+
if (
|
|
74
|
+
flowSchema.initiateBy === 'user' ||
|
|
75
|
+
flowSchema.initiateBy === 'systemWithUser'
|
|
76
|
+
) {
|
|
74
77
|
_izContext.correlationIds.set(coreConsts.USER_AGENT, 'webSocket');
|
|
75
78
|
}
|
|
76
79
|
|
|
@@ -78,6 +81,7 @@ export default async function <%- typeof functionName !== 'undefined' ? function
|
|
|
78
81
|
|
|
79
82
|
if (!flowSchema) {
|
|
80
83
|
await postToConnection(
|
|
84
|
+
_izContext,
|
|
81
85
|
{
|
|
82
86
|
message: {
|
|
83
87
|
error: `flowTag not found ${requestParams.action}`
|
|
@@ -99,7 +103,7 @@ export default async function <%- typeof functionName !== 'undefined' ? function
|
|
|
99
103
|
callingFlowSharedLib.createCallingFlowConfig(
|
|
100
104
|
await lambdaSharedLib.lambdaFunctionName(
|
|
101
105
|
_izContext,
|
|
102
|
-
'
|
|
106
|
+
'WebSocketCompleteHdrSqs'
|
|
103
107
|
),
|
|
104
108
|
{}
|
|
105
109
|
)
|
|
@@ -94,7 +94,7 @@ export default async function <%- flowTag %>(
|
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
<%_
|
|
97
|
-
const rbacObjects = objects.filter(o => (o
|
|
97
|
+
const rbacObjects = objects.filter(o => isCrudObject(o) && hasRbacAddOn(o));
|
|
98
98
|
const rbacObjectTypes = rbacObjects.map(o => o.objectType);
|
|
99
99
|
const rbacServiceTagMap = {};
|
|
100
100
|
for (const o of rbacObjects) {
|
|
@@ -23,6 +23,7 @@ export function supportedCustomFlows(flows) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function customFlowRoleName(flowTag) {
|
|
26
|
+
if (!flowTag || typeof flowTag !== 'string') return '';
|
|
26
27
|
return `${upperFirst(flowTag)}Role`;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -137,6 +138,7 @@ export function generatedDsqFlows(flows) {
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
export function upperFirst(value) {
|
|
141
|
+
if (typeof value !== 'string' || value.length === 0) return '';
|
|
140
142
|
return value[0].toUpperCase() + value.slice(1);
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -77,7 +77,7 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
77
77
|
} else {
|
|
78
78
|
const connId = event.connId;
|
|
79
79
|
// await postMessage({ message: event.message }, connId);
|
|
80
|
-
await postToConnection({ message: event.message }, connId);
|
|
80
|
+
await postToConnection(event._izContext, { message: event.message }, connId);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
return response.webSocketSuccess();
|
|
@@ -85,7 +85,7 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
85
85
|
const connId = event.requestContext.connectionId;
|
|
86
86
|
event._izContext.logger.error('Error, WebSocket: ', err);
|
|
87
87
|
// await sendErrorMessage({ message: err.message }, connId);
|
|
88
|
-
await postToConnection({ message: err.message }, connId);
|
|
88
|
+
await postToConnection(event._izContext, { message: err.message }, connId);
|
|
89
89
|
return response.failure(err);
|
|
90
90
|
}
|
|
91
91
|
});
|
|
@@ -171,6 +171,7 @@ export default async function createPreSignUrl(
|
|
|
171
171
|
await sns.publishAsync(_izContext, sendMessageToCancelReserved);
|
|
172
172
|
|
|
173
173
|
await postToConnection(
|
|
174
|
+
_izContext,
|
|
174
175
|
{ message: 'reserved has reach limit' },
|
|
175
176
|
connectionId
|
|
176
177
|
);
|
|
@@ -254,7 +255,7 @@ export default async function createPreSignUrl(
|
|
|
254
255
|
// calling flow own service
|
|
255
256
|
await lambdaSharedLib.lambdaFunctionName(
|
|
256
257
|
_izContext,
|
|
257
|
-
`
|
|
258
|
+
`WebSocketCompleteHdrSqs`
|
|
258
259
|
),
|
|
259
260
|
{}
|
|
260
261
|
)
|
|
@@ -89,7 +89,7 @@ export default async function reservedLimit(
|
|
|
89
89
|
_izContext.logger.debug('res::', res);
|
|
90
90
|
|
|
91
91
|
if (res.passStatus === false) {
|
|
92
|
-
postToConnection({ message: 'static limit over' }, connectionId);
|
|
92
|
+
await postToConnection(_izContext, { message: 'static limit over' }, connectionId);
|
|
93
93
|
let identifiers = hash({
|
|
94
94
|
//(<hashIdentifiers>)
|
|
95
95
|
//(</hashIdentifiers>)
|
|
@@ -93,7 +93,7 @@ async function _artifact(flowTag, flow) {
|
|
|
93
93
|
function _createSharedRbacFlow(flowTag, event) {
|
|
94
94
|
return {
|
|
95
95
|
flowTag,
|
|
96
|
-
initiateBy: '
|
|
96
|
+
initiateBy: 'systemWithNoUser',
|
|
97
97
|
statusType: 'none',
|
|
98
98
|
event,
|
|
99
99
|
outputTopic: true,
|
|
@@ -119,7 +119,7 @@ function _createUserRbacFlow(flowTag, serviceTag, objectType) {
|
|
|
119
119
|
flowTag,
|
|
120
120
|
objType: { serviceTag, objectType },
|
|
121
121
|
event: ['ownTopic'],
|
|
122
|
-
initiateBy: '
|
|
122
|
+
initiateBy: 'systemWithNoUser',
|
|
123
123
|
handleObj: 'one',
|
|
124
124
|
statusType: 'none',
|
|
125
125
|
outputTopic: true,
|
|
@@ -191,8 +191,15 @@ export default async function <%- functionName %>(
|
|
|
191
191
|
*/
|
|
192
192
|
// ? [Fix]: The legacy logic incorrectly blocked "user" from regular execution and allowed non-user values.
|
|
193
193
|
// ? It has been corrected to require changeBy === "user" for normal execution.
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
const isUserProcess = Boolean(_izContext.correlationIds?.get(consts.BASE_USER_ID));
|
|
195
|
+
if (isUserProcess) {
|
|
196
|
+
if (canChangeToRelType.changeBy !== 'user' && canChangeToRelType.changeBy !== 'systemWithUser') {
|
|
197
|
+
errorsFound.push(`CanChangeToRelType changeBy is not user or systemWithUser`);
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
if (canChangeToRelType.changeBy !== 'systemWithNoUser' && canChangeToRelType.changeBy !== 'system') {
|
|
201
|
+
errorsFound.push(`CanChangeToRelType changeBy is not systemWithNoUser`);
|
|
202
|
+
}
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
205
|
});
|
|
@@ -333,7 +333,8 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
333
333
|
(flow) =>
|
|
334
334
|
(flow?.outputTopic ||
|
|
335
335
|
(includesEvent(flow, 'ownTopic') &&
|
|
336
|
-
flow.initiateBy === 'user'
|
|
336
|
+
(flow.initiateBy === 'user' ||
|
|
337
|
+
flow.initiateBy === 'systemWithUser'))) &&
|
|
337
338
|
upperFirst(flow.flowTag) === topic
|
|
338
339
|
) ||
|
|
339
340
|
(relationships.length > 0 &&
|
|
@@ -899,11 +900,14 @@ function keyName(identifier) {
|
|
|
899
900
|
);
|
|
900
901
|
}
|
|
901
902
|
|
|
903
|
+
const VALID_CRUD_GENERATED_BY = new Set([
|
|
904
|
+
'user',
|
|
905
|
+
'systemWithUser',
|
|
906
|
+
'systemWithNoUser'
|
|
907
|
+
]);
|
|
908
|
+
|
|
902
909
|
function isCrudObject(object) {
|
|
903
|
-
return (
|
|
904
|
-
object?.generatedBy === 'userGenerated' ||
|
|
905
|
-
object?.generatedBy === 'systemGenerated'
|
|
906
|
-
);
|
|
910
|
+
return VALID_CRUD_GENERATED_BY.has(object?.generatedBy);
|
|
907
911
|
}
|
|
908
912
|
function hasSelfRbacAddOn(object) {
|
|
909
913
|
return (
|
|
@@ -125,7 +125,8 @@ export function hasSystemWebSocket(
|
|
|
125
125
|
(flow.event.includes('s3') ||
|
|
126
126
|
(includeRelationshipOwnTopic &&
|
|
127
127
|
flow.event.includes('ownTopic') &&
|
|
128
|
-
flow.initiateBy === 'user'
|
|
128
|
+
(flow.initiateBy === 'user' ||
|
|
129
|
+
flow.initiateBy === 'systemWithUser')))
|
|
129
130
|
)
|
|
130
131
|
);
|
|
131
132
|
}
|
|
@@ -93,9 +93,9 @@ export default async function registerCompleteMain(
|
|
|
93
93
|
await Promise.all(
|
|
94
94
|
tasks.Items.map(async (task) => {
|
|
95
95
|
if (!result) {
|
|
96
|
-
await postToConnection({ message: 'not have record' }, task.connectionId);
|
|
96
|
+
await postToConnection(_izContext, { message: 'not have record' }, task.connectionId);
|
|
97
97
|
} else {
|
|
98
|
-
await postToConnection({ message: result }, task.connectionId);
|
|
98
|
+
await postToConnection(_izContext, { message: result }, task.connectionId);
|
|
99
99
|
}
|
|
100
100
|
})
|
|
101
101
|
);
|
|
@@ -138,7 +138,7 @@ export default async function registerCompleteMain(
|
|
|
138
138
|
if (tasks.Items && tasks.Items.length > 0) {
|
|
139
139
|
await Promise.all(
|
|
140
140
|
tasks.Items.map(async (task) => {
|
|
141
|
-
await postToConnection({ message: task }, task.connectionId);
|
|
141
|
+
await postToConnection(_izContext, { message: task }, task.connectionId);
|
|
142
142
|
})
|
|
143
143
|
);
|
|
144
144
|
}
|
|
@@ -42,14 +42,14 @@ export const main = middleware.wrap(async (event, context) => {
|
|
|
42
42
|
}
|
|
43
43
|
} else {
|
|
44
44
|
const connId = event.connId;
|
|
45
|
-
await postToConnection({ message: event.message }, connId);
|
|
45
|
+
await postToConnection(event._izContext, { message: event.message }, connId);
|
|
46
46
|
}
|
|
47
47
|
return response.webSocketSuccess();
|
|
48
48
|
} catch (err) {
|
|
49
49
|
const connId = event.requestContext?.connectionId;
|
|
50
50
|
event._izContext.logger.error('Error, WebSocket: ', err);
|
|
51
51
|
if (connId) {
|
|
52
|
-
await postToConnection({ message: err.message }, connId);
|
|
52
|
+
await postToConnection(event._izContext, { message: err.message }, connId);
|
|
53
53
|
}
|
|
54
54
|
return response.failure(err);
|
|
55
55
|
}
|
|
@@ -55,7 +55,7 @@ export default async function registerMain(
|
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
if (!result) {
|
|
58
|
-
await postToConnection({ message: 'not have record' }, connectionId);
|
|
58
|
+
await postToConnection(_izContext, { message: 'not have record' }, connectionId);
|
|
59
59
|
} else {
|
|
60
60
|
for (const [fieldName, fieldNameSetting] of Object.entries(
|
|
61
61
|
objectSchema.fieldNames
|
|
@@ -77,7 +77,7 @@ export default async function registerMain(
|
|
|
77
77
|
}
|
|
78
78
|
);
|
|
79
79
|
} else {
|
|
80
|
-
await postToConnection({ message: result }, connectionId);
|
|
80
|
+
await postToConnection(_izContext, { message: result }, connectionId);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -121,10 +121,10 @@ export default async function registerMain(
|
|
|
121
121
|
if (tasks.Items && tasks.Items.length > 0) {
|
|
122
122
|
await Promise.all(
|
|
123
123
|
tasks.Items.map(async (task) => {
|
|
124
|
-
await postToConnection({ message: task }, task.connectionId);
|
|
124
|
+
await postToConnection(_izContext, { message: task }, task.connectionId);
|
|
125
125
|
})
|
|
126
126
|
);
|
|
127
|
-
await postToConnection({ message: tasks.Items[0] }, connectionId);
|
|
127
|
+
await postToConnection(_izContext, { message: tasks.Items[0] }, connectionId);
|
|
128
128
|
return tasks.Items[0];
|
|
129
129
|
} else {
|
|
130
130
|
await dynamodbSharedLib.putItem(
|
|
@@ -76,7 +76,7 @@ export default async function WebSocketComplete(
|
|
|
76
76
|
(tasks.Items ?? []).map(async (task) => {
|
|
77
77
|
//(<beforePostMessage>)
|
|
78
78
|
//(</beforePostMessage>)
|
|
79
|
-
await postToConnection({ message: requestParams }, task.connectionId);
|
|
79
|
+
await postToConnection(_izContext, { message: requestParams }, task.connectionId);
|
|
80
80
|
await dynamodbSharedLib.deleteItem(
|
|
81
81
|
_izContext,
|
|
82
82
|
await dynamodbSharedLib.tableName(_izContext, 'WebSocketTask'),
|
|
@@ -84,8 +84,8 @@ export async function emitSystemTextSchemas({
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
for (const fieldName of fieldNames) {
|
|
87
|
-
const nameSpace = `${objectType}
|
|
88
|
-
const systemTextTag = `${fieldName}
|
|
87
|
+
const nameSpace = `${objectType}|${objServiceTag}|description`;
|
|
88
|
+
const systemTextTag = `${fieldName}|${objectType}|${objServiceTag}`;
|
|
89
89
|
const openHook = `//(<${objectType}_${objServiceTag}${fieldName}Hook>)`;
|
|
90
90
|
const closeHook = `//(</${objectType}_${objServiceTag}${fieldName}Hook>)`;
|
|
91
91
|
datas.push({
|
|
@@ -100,7 +100,7 @@ export async function emitSystemTextSchemas({
|
|
|
100
100
|
// 2. Process Notifications
|
|
101
101
|
for (const notification of safeNotifications) {
|
|
102
102
|
if (notification?.notificationGrouping && notification?.notificationTag) {
|
|
103
|
-
const nameSpace = `
|
|
103
|
+
const nameSpace = `sysNotification|${notification.notificationGrouping}`;
|
|
104
104
|
const systemTextTag = notification.notificationTag;
|
|
105
105
|
const openHook = `//(<${notification.notificationTag}Hook>)`;
|
|
106
106
|
const closeHook = `//(</${notification.notificationTag}Hook>)`;
|
|
@@ -127,8 +127,8 @@ export async function emitSystemTextSchemas({
|
|
|
127
127
|
|
|
128
128
|
if (relData?.fieldNames && Object.keys(relData.fieldNames).length > 0) {
|
|
129
129
|
for (const fieldName of Object.keys(relData.fieldNames)) {
|
|
130
|
-
const nameSpace = `${relTag}
|
|
131
|
-
const systemTextTag = `${fieldName}
|
|
130
|
+
const nameSpace = `${relTag}|${relServiceTag}|description`;
|
|
131
|
+
const systemTextTag = `${fieldName}|${relTag}|${relServiceTag}`;
|
|
132
132
|
const openHook = `//(<${relTag}_${relServiceTag}${fieldName}Hook>)`;
|
|
133
133
|
const closeHook = `//(</${relTag}_${relServiceTag}${fieldName}Hook>)`;
|
|
134
134
|
datas.push({
|
|
@@ -177,8 +177,9 @@ export async function emitSystemTextSchemas({
|
|
|
177
177
|
// Dynamic CRUD flows
|
|
178
178
|
const hasCrudObjects = safeObjects.some(
|
|
179
179
|
(object) =>
|
|
180
|
-
object?.generatedBy === '
|
|
181
|
-
object?.generatedBy === '
|
|
180
|
+
object?.generatedBy === 'user' ||
|
|
181
|
+
object?.generatedBy === 'systemWithUser' ||
|
|
182
|
+
object?.generatedBy === 'systemWithNoUser'
|
|
182
183
|
);
|
|
183
184
|
if (hasCrudObjects) {
|
|
184
185
|
for (const action of CRUD_ACTIONS) {
|
|
@@ -208,8 +209,8 @@ export async function emitSystemTextSchemas({
|
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
for (const flowTag of flowTags) {
|
|
211
|
-
const nameSpace = `${flowTag}
|
|
212
|
-
const systemTextTag = `
|
|
212
|
+
const nameSpace = `${flowTag}|${serviceTag}`;
|
|
213
|
+
const systemTextTag = `errors|${flowTag}|${serviceTag}`;
|
|
213
214
|
const openHook = `//(<${flowTag}_${serviceTag}Hook>)`;
|
|
214
215
|
const closeHook = `//(</${flowTag}_${serviceTag}Hook>)`;
|
|
215
216
|
datas.push({
|
|
@@ -67,16 +67,27 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
|
|
|
67
67
|
),
|
|
68
68
|
/checkAllAwaitingStepsFinishedWithReturnParams/
|
|
69
69
|
);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'app/src/generatedCode/FlowObjects/Create/source/Create_WbsMain.js'
|
|
75
|
-
),
|
|
76
|
-
'utf8'
|
|
70
|
+
const createWbsMain = await readFile(
|
|
71
|
+
join(
|
|
72
|
+
rootPath,
|
|
73
|
+
'app/src/generatedCode/FlowObjects/Create/source/Create_WbsMain.js'
|
|
77
74
|
),
|
|
75
|
+
'utf8'
|
|
76
|
+
);
|
|
77
|
+
assert.doesNotMatch(
|
|
78
|
+
createWbsMain,
|
|
78
79
|
/from '@izara_project\/izara-core-generate-service-code'/
|
|
79
80
|
);
|
|
81
|
+
assert.match(createWbsMain, /'WebSocketCompleteHdrSqs'/);
|
|
82
|
+
assert.match(createWbsMain, /postToConnection\(\s*_izContext,/);
|
|
83
|
+
const createHdrWbs = await readFile(
|
|
84
|
+
join(
|
|
85
|
+
rootPath,
|
|
86
|
+
'app/src/generatedCode/FlowObjects/Create/source/Create_HdrWbs.js'
|
|
87
|
+
),
|
|
88
|
+
'utf8'
|
|
89
|
+
);
|
|
90
|
+
assert.match(createHdrWbs, /postToConnection\(event\._izContext,/);
|
|
80
91
|
await assert.rejects(
|
|
81
92
|
readFile(
|
|
82
93
|
join(
|
|
@@ -269,7 +280,7 @@ test('emits GetRangeHdrApi when an object defines a sortKey identifier', async (
|
|
|
269
280
|
const rootPath = await createProject(true);
|
|
270
281
|
await writeFile(
|
|
271
282
|
join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
|
|
272
|
-
"export default { objectType: 'order', generatedBy: '
|
|
283
|
+
"export default { objectType: 'order', generatedBy: 'user', identifiers: [{ type: 'identifier', fieldName: 'orderId' }, { type: 'sortKey', fieldName: 'createdAt' }], storageResources: { graph: { storageType: 'graph' } } };\n"
|
|
273
284
|
);
|
|
274
285
|
const result = await generateCrudApplicationArtifacts(rootPath, {
|
|
275
286
|
serviceTag: 'orders'
|
|
@@ -341,7 +352,7 @@ async function createProject(withCrudObject) {
|
|
|
341
352
|
await writeFile(
|
|
342
353
|
join(schemaRoot, 'ObjectSchemas/Order.js'),
|
|
343
354
|
withCrudObject
|
|
344
|
-
? "export default { objectType: 'order', generatedBy: '
|
|
355
|
+
? "export default { objectType: 'order', generatedBy: 'user', storageResources: { graph: { storageType: 'graph' } } };\n"
|
|
345
356
|
: "export default { objectType: 'order' };\n"
|
|
346
357
|
);
|
|
347
358
|
return rootPath;
|
|
@@ -357,7 +368,7 @@ async function createRbacProject(withHook = false, withGraph = false) {
|
|
|
357
368
|
await mkdir(join(schemaRoot, 'FlowSchemas'), { recursive: true });
|
|
358
369
|
await writeFile(
|
|
359
370
|
join(schemaRoot, 'ObjectSchemas/Order.js'),
|
|
360
|
-
`export default { objectType: 'order', generatedBy: '
|
|
371
|
+
`export default { objectType: 'order', generatedBy: 'user', belongTo: { serviceTag: 'UserAccount', objectType: 'user' }${withGraph ? ", storageResources: { graph: { storageType: 'graph' } }" : ''} };\n`
|
|
361
372
|
);
|
|
362
373
|
if (withHook) {
|
|
363
374
|
await writeFile(
|
|
@@ -239,6 +239,14 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
|
|
|
239
239
|
assert.ok(helperPath, `${path} imports GenerateCodeLibs`);
|
|
240
240
|
await access(resolve(dirname(filePath), helperPath));
|
|
241
241
|
}
|
|
242
|
+
const presignMain = await readFile(
|
|
243
|
+
join(
|
|
244
|
+
rootPath,
|
|
245
|
+
'app/src/generatedCode/FlowSchemas/Upload/source/UploadCreatePreSignUrl_Main.js'
|
|
246
|
+
),
|
|
247
|
+
'utf8'
|
|
248
|
+
);
|
|
249
|
+
assert.match(presignMain, /`WebSocketCompleteHdrSqs`/);
|
|
242
250
|
});
|
|
243
251
|
|
|
244
252
|
test('removes only V2-owned custom-flow application artifacts when supported events are removed', async () => {
|
|
@@ -9,7 +9,7 @@ import { generateDynamicCrudFlowSchemas } from '../src/emit/crud/generateDynamic
|
|
|
9
9
|
|
|
10
10
|
test('generates dynamic CRUD schemas from local schema modules', async () => {
|
|
11
11
|
const rootPath = await createProject({
|
|
12
|
-
generatedBy: '
|
|
12
|
+
generatedBy: 'user'
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const result = await generateDynamicCrudFlowSchemas(rootPath);
|
|
@@ -41,7 +41,7 @@ test('generates dynamic CRUD schemas from local schema modules', async () => {
|
|
|
41
41
|
|
|
42
42
|
test('accepts singleton object exports used by existing schema files', async () => {
|
|
43
43
|
const rootPath = await createProject({
|
|
44
|
-
generatedBy: '
|
|
44
|
+
generatedBy: 'user',
|
|
45
45
|
singletonExports: true
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -57,7 +57,7 @@ test('accepts singleton object exports used by existing schema files', async ()
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test('does not generate CRUD schemas without an applicable object', async () => {
|
|
60
|
-
const rootPath = await createProject({ generatedBy: '
|
|
60
|
+
const rootPath = await createProject({ generatedBy: 'user' });
|
|
61
61
|
await generateDynamicCrudFlowSchemas(rootPath);
|
|
62
62
|
await writeFile(
|
|
63
63
|
join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
|
|
@@ -71,7 +71,7 @@ test('does not generate CRUD schemas without an applicable object', async () =>
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
test('does not allow options to override the rootPath argument', async () => {
|
|
74
|
-
const rootPath = await createProject({ generatedBy: '
|
|
74
|
+
const rootPath = await createProject({ generatedBy: 'user' });
|
|
75
75
|
const ignoredRootPath = await createProject({ generatedBy: 'external' });
|
|
76
76
|
|
|
77
77
|
const result = await generateDynamicCrudFlowSchemas(rootPath, {
|
|
@@ -101,7 +101,7 @@ test('rejects source modules that do not default-export schema objects or arrays
|
|
|
101
101
|
|
|
102
102
|
test('rejects deprecated FlowHook schemas before generating CRUD schemas', async () => {
|
|
103
103
|
const rootPath = await createProject({
|
|
104
|
-
generatedBy: '
|
|
104
|
+
generatedBy: 'systemWithNoUser',
|
|
105
105
|
flow: createHookFlow(['ownTopic'])
|
|
106
106
|
});
|
|
107
107
|
|
|
@@ -121,7 +121,7 @@ test('generates a direct Complete step for RBAC-only Update flows', async () =>
|
|
|
121
121
|
join(objectDirectory, 'Order.js'),
|
|
122
122
|
`export default [{
|
|
123
123
|
objectType: 'order',
|
|
124
|
-
generatedBy: '
|
|
124
|
+
generatedBy: 'user',
|
|
125
125
|
belongTo: { serviceTag: 'UserAccount', objectType: 'user' }
|
|
126
126
|
}];\n`
|
|
127
127
|
);
|
|
@@ -143,13 +143,13 @@ test('generates a direct Complete step for RBAC-only Update flows', async () =>
|
|
|
143
143
|
|
|
144
144
|
test('ignores RBAC for Create even when Graph storage is present', async () => {
|
|
145
145
|
const rootPath = await createProject({
|
|
146
|
-
generatedBy: '
|
|
146
|
+
generatedBy: 'user',
|
|
147
147
|
});
|
|
148
148
|
await writeFile(
|
|
149
149
|
join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
|
|
150
150
|
`export default [{
|
|
151
151
|
objectType: 'order',
|
|
152
|
-
generatedBy: '
|
|
152
|
+
generatedBy: 'user',
|
|
153
153
|
belongTo: { serviceTag: 'UserAccount', objectType: 'user' }
|
|
154
154
|
, storageResources: { graph: { storageType: 'graph' } }
|
|
155
155
|
}];\n`
|
|
@@ -173,7 +173,7 @@ test('uses the renamed RBAC callback step when Graph storage is present', async
|
|
|
173
173
|
join(objectDirectory, 'Order.js'),
|
|
174
174
|
`export default [{
|
|
175
175
|
objectType: 'order',
|
|
176
|
-
generatedBy: '
|
|
176
|
+
generatedBy: 'user',
|
|
177
177
|
belongTo: { serviceTag: 'UserAccount', objectType: 'user' },
|
|
178
178
|
storageResources: { graph: { storageType: 'graph' } }
|
|
179
179
|
}];\n`
|
|
@@ -204,7 +204,7 @@ test('chains RbacCallback and Complete for Update with RBAC and Graph', async ()
|
|
|
204
204
|
join(objectDirectory, 'Order.js'),
|
|
205
205
|
`export default [{
|
|
206
206
|
objectType: 'order',
|
|
207
|
-
generatedBy: '
|
|
207
|
+
generatedBy: 'user',
|
|
208
208
|
belongTo: { serviceTag: 'UserAccount', objectType: 'user' },
|
|
209
209
|
storageResources: { graph: { storageType: 'graph' } }
|
|
210
210
|
}];\n`
|
|
@@ -242,7 +242,7 @@ test('omits Complete step when no graph storage resources exist', async () => {
|
|
|
242
242
|
join(objectDirectory, 'Order.js'),
|
|
243
243
|
`export default [{
|
|
244
244
|
objectType: 'order',
|
|
245
|
-
generatedBy: '
|
|
245
|
+
generatedBy: 'user',
|
|
246
246
|
storageResources: {
|
|
247
247
|
myTable: { storageType: 'dynamoDB', tableName: 'OrderTable' }
|
|
248
248
|
}
|
|
@@ -318,3 +318,13 @@ function readGeneratedFile(rootPath, action) {
|
|
|
318
318
|
'utf8'
|
|
319
319
|
);
|
|
320
320
|
}
|
|
321
|
+
|
|
322
|
+
test('supports new standard generatedBy values (user, systemWithUser, systemWithNoUser)', async () => {
|
|
323
|
+
for (const generatedBy of ['user', 'systemWithUser', 'systemWithNoUser']) {
|
|
324
|
+
const rootPath = await createProject({ generatedBy });
|
|
325
|
+
await generateDynamicCrudFlowSchemas(rootPath);
|
|
326
|
+
const create = await readGeneratedFlow(rootPath, 'Create');
|
|
327
|
+
assert.ok(create, `Create flow should be generated for generatedBy: ${generatedBy}`);
|
|
328
|
+
assert.deepEqual(create.flowSteps.map((s) => s.stepName), ['Start', 'Complete']);
|
|
329
|
+
}
|
|
330
|
+
});
|
package/test/publicApi.test.js
CHANGED
|
@@ -33,7 +33,7 @@ test('generate commits schemas, application artifacts, and resources in one publ
|
|
|
33
33
|
),
|
|
34
34
|
'utf8'
|
|
35
35
|
),
|
|
36
|
-
/nameSpace:\s*"
|
|
36
|
+
/nameSpace:\s*"Create\|/
|
|
37
37
|
);
|
|
38
38
|
assert.match(
|
|
39
39
|
await readFile(
|
|
@@ -203,6 +203,7 @@ test('generate commits the system WebSocket completion path for own-topic flows'
|
|
|
203
203
|
);
|
|
204
204
|
assert.match(webSocketComplete, /\(\<createIdentifiersFlow\>\)/);
|
|
205
205
|
assert.match(webSocketComplete, /\(\<beforePostMessage\>\)/);
|
|
206
|
+
assert.match(webSocketComplete, /postToConnection\(_izContext,/);
|
|
206
207
|
const webSocketHandler = await readFile(
|
|
207
208
|
join(
|
|
208
209
|
rootPath,
|
|
@@ -345,7 +346,7 @@ async function createCrudProject() {
|
|
|
345
346
|
await mkdir(objectDirectory, { recursive: true });
|
|
346
347
|
await writeFile(
|
|
347
348
|
join(objectDirectory, 'Order.js'),
|
|
348
|
-
"export default { objectType: 'order', generatedBy: '
|
|
349
|
+
"export default { objectType: 'order', generatedBy: 'user', storageResources: { graph: { storageType: 'graph' } } };\n"
|
|
349
350
|
);
|
|
350
351
|
return rootPath;
|
|
351
352
|
}
|
|
@@ -250,7 +250,7 @@ test('emits DynamoDB table with number sortKey attribute type when field type is
|
|
|
250
250
|
join(rootPath, 'app/src/schemas/ObjectSchemas/CartOrder.js'),
|
|
251
251
|
"export default { " +
|
|
252
252
|
"objectType: 'cartOrder', " +
|
|
253
|
-
"generatedBy: '
|
|
253
|
+
"generatedBy: 'user', " +
|
|
254
254
|
"fieldNames: { " +
|
|
255
255
|
" cartId: { type: 'string' }, " +
|
|
256
256
|
" sortId: { type: 'number' } " +
|
|
@@ -329,7 +329,7 @@ async function createProject() {
|
|
|
329
329
|
);
|
|
330
330
|
await writeFile(
|
|
331
331
|
join(schemas, 'ObjectSchemas/Order.js'),
|
|
332
|
-
"export default { objectType: 'order', generatedBy: '
|
|
332
|
+
"export default { objectType: 'order', generatedBy: 'user', addOnDataStructure: [{ type: 'rbac' }], identifiers: [{ type: 'partitionKey', fieldName: 'orderId' }], storageResources: { main: { storageType: 'dynamoDB', tableName: 'Orders' } } };\n"
|
|
333
333
|
);
|
|
334
334
|
await writeFile(
|
|
335
335
|
join(schemas, 'RelationshipSchemas/OrderCustomer.js'),
|
|
@@ -63,32 +63,32 @@ test('emitSystemTextSchemas produces expected structure for objects, flows, rela
|
|
|
63
63
|
|
|
64
64
|
const content = artifacts[0].content;
|
|
65
65
|
// Object fields
|
|
66
|
-
assert.match(content, /nameSpace:\s*"
|
|
66
|
+
assert.match(content, /nameSpace:\s*"product\|MyService\|description"/);
|
|
67
67
|
assert.match(
|
|
68
68
|
content,
|
|
69
|
-
/systemTextTag:\s*"
|
|
69
|
+
/systemTextTag:\s*"productId\|product\|MyService"/
|
|
70
70
|
);
|
|
71
71
|
assert.match(content, /\/\/\(<product_MyServiceproductIdHook>\)/);
|
|
72
72
|
assert.match(
|
|
73
73
|
content,
|
|
74
|
-
/systemTextTag:\s*"
|
|
74
|
+
/systemTextTag:\s*"version\|product\|MyService"/
|
|
75
75
|
);
|
|
76
76
|
|
|
77
77
|
// Notification
|
|
78
|
-
assert.match(content, /nameSpace:\s*"
|
|
78
|
+
assert.match(content, /nameSpace:\s*"sysNotification\|OrderEvents"/);
|
|
79
79
|
assert.match(content, /systemTextTag:\s*"OrderPlaced"/);
|
|
80
80
|
assert.match(content, /\/\/\(<OrderPlacedHook>\)/);
|
|
81
81
|
|
|
82
82
|
// Relationship
|
|
83
|
-
assert.match(content, /nameSpace:\s*"
|
|
83
|
+
assert.match(content, /nameSpace:\s*"hasCategory\|MyService\|description"/);
|
|
84
84
|
assert.match(
|
|
85
85
|
content,
|
|
86
|
-
/systemTextTag:\s*"
|
|
86
|
+
/systemTextTag:\s*"originTimestamp\|hasCategory\|MyService"/
|
|
87
87
|
);
|
|
88
88
|
|
|
89
89
|
// Custom Flow
|
|
90
|
-
assert.match(content, /nameSpace:\s*"
|
|
91
|
-
assert.match(content, /systemTextTag:\s*"
|
|
90
|
+
assert.match(content, /nameSpace:\s*"ProcessOrder\|MyService"/);
|
|
91
|
+
assert.match(content, /systemTextTag:\s*"errors\|ProcessOrder\|MyService"/);
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
test('emitSystemTextSchemas returns empty array when no inputs provided', async () => {
|
|
@@ -123,7 +123,7 @@ test('generate preserves user hooks inside SystemTextSchemas.js across regenerat
|
|
|
123
123
|
);
|
|
124
124
|
await writeFile(
|
|
125
125
|
join(rootPath, 'app/src/schemas/ObjectSchemas/Item.js'),
|
|
126
|
-
"export default { objectType: 'item', fieldNames: { itemId: { type: 'string' } }, generatedBy: '
|
|
126
|
+
"export default { objectType: 'item', fieldNames: { itemId: { type: 'string' } }, generatedBy: 'user' };\n"
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
// First generate
|