@izara_project/izara-core-generate-service-code 1.0.82 → 1.0.84

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.
Files changed (60) hide show
  1. package/package.json +4 -4
  2. package/src/compile/flowValidator.js +34 -0
  3. package/src/compile/loadSchemas.js +121 -7
  4. package/src/emit/attributeTree/emitAttributeTreeSchemas.js +388 -0
  5. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +29 -1
  6. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +16 -3
  7. package/src/emit/crud/crudActionPlan.js +43 -7
  8. package/src/emit/crud/emitCrudApplicationArtifacts.js +19 -4
  9. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +27 -6
  10. package/src/emit/crud/generateCrudApplicationArtifacts.js +3 -2
  11. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +3 -2
  12. package/src/emit/crud/loadLocalSchemaArrays.js +3 -3
  13. package/src/emit/crud/templates/CreateComplete_Main.ejs +1 -1
  14. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +75 -21
  15. package/src/emit/crud/templates/CreateLogicalLib.ejs +73 -8
  16. package/src/emit/crud/templates/DeleteComplete_Main.ejs +1 -1
  17. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +39 -11
  18. package/src/emit/crud/templates/DeleteLogicalLib.ejs +6 -3
  19. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +2 -2
  20. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -0
  21. package/src/emit/crud/templates/FlowMain_Wbs.ejs +5 -2
  22. package/src/emit/crud/templates/GetEndpoint_Main.ejs +13 -0
  23. package/src/emit/crud/templates/UpdateComplete_Main.ejs +1 -1
  24. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +53 -4
  25. package/src/emit/crud/templates/UpdateLogicalLib.ejs +4 -3
  26. package/src/emit/custom/customFlows.js +49 -0
  27. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +168 -49
  28. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +1 -1
  29. package/src/emit/custom/templates/FlowEndpointRbacCallback_Main.ejs +194 -0
  30. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +14 -22
  31. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  32. package/src/emit/function/templates/WbsFunction_Yaml.ejs +0 -2
  33. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +33 -8
  34. package/src/emit/rbac/emitRbacApplicationArtifacts.js +204 -7
  35. package/src/emit/rbac/templates/CancelUsage_Main.ejs +103 -0
  36. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +71 -0
  37. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +49 -6
  38. package/src/emit/rbac/templates/ListRbac_Main.ejs +9 -3
  39. package/src/emit/rbac/templates/RbacMutation_Main.ejs +1 -1
  40. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +147 -0
  41. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +59 -23
  42. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +1 -1
  43. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +1 -1
  44. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +10 -3
  45. package/src/emit/resource/emitResourceArtifacts.js +350 -112
  46. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +53 -2
  47. package/src/emit/system/emitSystemWebSocketArtifacts.js +6 -4
  48. package/src/emit/system/templates/WebSocketConnect.ejs +71 -12
  49. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +7 -0
  50. package/src/emit/systemText/emitSystemTextSchemas.js +3 -2
  51. package/src/generate.js +41 -10
  52. package/test/attributeTreeSchemas.test.js +219 -0
  53. package/test/createTargetRoleHandler.test.js +59 -4
  54. package/test/crudApplicationArtifacts.test.js +23 -6
  55. package/test/customFlowApplicationArtifacts.test.js +5 -5
  56. package/test/dynamicCrudFlowSchemas.test.js +110 -13
  57. package/test/dynamicRbacFlowSchemas.test.js +15 -2
  58. package/test/publicApi.test.js +7 -7
  59. package/test/resourceArtifacts.test.js +70 -29
  60. package/test/systemTextSchemas.test.js +1 -1
@@ -70,6 +70,10 @@ const externalServices = {
70
70
  dynamoTableNames.add('RolePermissions');
71
71
  dynamoTableNames.add('RoleUsers');
72
72
  dynamoTableNames.add('UserRoles');
73
+ dynamoTableNames.add('RbacPermissionCaches');
74
+ dynamoTableNames.add('RoleLimit');
75
+ dynamoTableNames.add('RunningTotalUsage');
76
+ dynamoTableNames.add('RunningTotalReservedUsage');
73
77
  }
74
78
  if (typeof s3Flows !== 'undefined' && (s3Flows || []).length > 0) {
75
79
  dynamoTableNames.add('ReservedDataMain');
@@ -176,16 +180,25 @@ const tableConfigs = {
176
180
  if (crud && typeof rbacValidatorServiceTags !== 'undefined' && rbacValidatorServiceTags && rbacValidatorServiceTags.length > 0) {
177
181
  const allRbacTags = Array.from(new Set(['Rbac', ...rbacValidatorServiceTags]));
178
182
  const hasGraph = typeof objectGraphServiceTags !== 'undefined' && objectGraphServiceTags && objectGraphServiceTags.length > 0;
183
+ const createEndpoint = hasGraph ? 'CreateRbacCallbackHdrSqs' : 'CreateCompleteHdrSqs';
179
184
  const updateEndpoint = hasGraph ? 'UpdateRbacCallbackHdrSqs' : 'UpdateCompleteHdrSqs';
180
185
  const deleteEndpoint = hasGraph ? 'DeleteRbacCallbackHdrSqs' : 'DeleteCompleteHdrSqs';
181
186
  allRbacTags.forEach(function(tag) {
182
- if (!hasGraph) {
183
- rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: 'CreateCompleteHdrSqs' });
184
- }
187
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: createEndpoint });
185
188
  rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: updateEndpoint });
186
189
  rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: deleteEndpoint });
187
190
  });
188
191
  }
192
+
193
+ if (typeof customRbacFlowTags !== 'undefined' && customRbacFlowTags && customRbacFlowTags.length > 0) {
194
+ const allRbacTags = Array.from(new Set(['Rbac', ...(typeof rbacValidatorServiceTags !== 'undefined' ? rbacValidatorServiceTags : [])]));
195
+ customRbacFlowTags.forEach(function(flowTag) {
196
+ const tagUpper = flowTag[0].toUpperCase() + flowTag.slice(1);
197
+ allRbacTags.forEach(function(tag) {
198
+ rbacSubs.push({ serviceTag: tag, topicName: 'ValidatorRbac_Out', sqsEndpoint: `${tagUpper}RbacCallbackHdrSqs` });
199
+ });
200
+ });
201
+ }
189
202
  -%>
190
203
  const snsTopicSubscriptions = [
191
204
  <% if (relSubs.length > 0) { -%>
@@ -5,37 +5,45 @@ export function createCrudActionPlans(objects = []) {
5
5
  const hasRbac = objects.some(
6
6
  (object) => isCrudObject(object) && hasRbacObject(object, objects)
7
7
  );
8
+ const isBillable = objects.some(
9
+ (object) => isCrudObject(object) && isBillableObject(object, objects)
10
+ );
8
11
 
9
12
  return Object.fromEntries(
10
13
  CRUD_ACTIONS.map((action) => [
11
14
  action,
12
- createCrudActionPlan(action, { hasGraph, hasRbac })
15
+ createCrudActionPlan(action, { hasGraph, hasRbac, isBillable })
13
16
  ])
14
17
  );
15
18
  }
16
19
 
17
20
  export function createCrudActionPlan(
18
21
  action,
19
- { hasGraph = false, hasRbac = false } = {}
22
+ { hasGraph = false, hasRbac = false, isBillable = true } = {}
20
23
  ) {
21
- const actionHasRbac = action !== 'Create' && action !== 'Get' && hasRbac;
24
+ const actionHasRbac = action !== 'Get' && hasRbac;
22
25
  const outputTopic = action !== 'Get';
26
+ const hasQuotaReservation = action === 'Create' && hasRbac && isBillable;
23
27
 
24
28
  return {
25
29
  action,
26
30
  outputTopic,
27
31
  hasGraph: outputTopic && hasGraph,
28
32
  hasRbac: actionHasRbac,
33
+ hasQuotaReservation,
29
34
  hasComplete: outputTopic && (actionHasRbac || hasGraph),
30
35
  hasRbacCallback: outputTopic && actionHasRbac && hasGraph
31
36
  };
32
37
  }
33
38
 
39
+ export const VALID_CRUD_GENERATED_BY = new Set([
40
+ 'user',
41
+ 'systemWithUser',
42
+ 'systemNoUser'
43
+ ]);
44
+
34
45
  export function isCrudObject(object) {
35
- return (
36
- object?.generatedBy === 'userGenerated' ||
37
- object?.generatedBy === 'systemGenerated'
38
- );
46
+ return VALID_CRUD_GENERATED_BY.has(object?.generatedBy);
39
47
  }
40
48
 
41
49
  export function hasGraphStorageResource(objects = []) {
@@ -80,3 +88,31 @@ function normalizeBelongTo(object) {
80
88
  if (!object?.belongTo) return [];
81
89
  return Array.isArray(object.belongTo) ? object.belongTo : [object.belongTo];
82
90
  }
91
+
92
+ export function isBillableObject(object, objects = []) {
93
+ if (!isCrudObject(object)) return false;
94
+
95
+ const targets = normalizeBelongTo(object);
96
+ if (targets.length > 0) {
97
+ for (const target of targets) {
98
+ if (typeof target?.billable === 'boolean') {
99
+ return target.billable;
100
+ }
101
+ }
102
+ }
103
+
104
+ if (Array.isArray(object?.addOnDataStructure)) {
105
+ const rbacAddOn = object.addOnDataStructure.find(
106
+ (addOn) => addOn?.type === 'rbac'
107
+ );
108
+ if (typeof rbacAddOn?.billable === 'boolean') {
109
+ return rbacAddOn.billable;
110
+ }
111
+ }
112
+
113
+ if (typeof object?.billable === 'boolean') {
114
+ return object.billable;
115
+ }
116
+
117
+ return true;
118
+ }
@@ -11,7 +11,8 @@ import {
11
11
  import {
12
12
  CRUD_ACTIONS,
13
13
  createCrudActionPlans,
14
- isCrudObject
14
+ isCrudObject,
15
+ VALID_CRUD_GENERATED_BY
15
16
  } from './crudActionPlan.js';
16
17
 
17
18
  const ACTIONS = CRUD_ACTIONS;
@@ -22,12 +23,26 @@ const SHARED_TEMPLATE_URL = new URL('../shared/templates/', import.meta.url);
22
23
  export async function emitCrudApplicationArtifacts({
23
24
  objects,
24
25
  flows = [],
25
- serviceTag
26
+ serviceTag,
27
+ logger
26
28
  }) {
27
29
  if (!Array.isArray(objects) || !Array.isArray(flows)) {
28
30
  throw new TypeError('objects and flows must be arrays');
29
31
  }
30
- if (!objects.some(isCrudObject)) return [];
32
+ if (!objects.some(isCrudObject)) {
33
+ if (objects.length > 0) {
34
+ const summary = objects.map((obj) => ({
35
+ objectType: obj?.objectType ?? '(undefined)',
36
+ generatedBy: obj?.generatedBy ?? '(missing)'
37
+ }));
38
+ logger?.warn?.(
39
+ `[WARN] [CRUD Application] Skipped FlowObjects application artifacts generation: No valid CRUD ObjectSchema found. ` +
40
+ `Checked ${objects.length} object(s): ${JSON.stringify(summary)}. ` +
41
+ `Expected "generatedBy" to be one of: ${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`
42
+ );
43
+ }
44
+ return [];
45
+ }
31
46
 
32
47
  const handlers = await Promise.all(
33
48
  ACTIONS.map((action) => emitAction(action, objects, serviceTag))
@@ -87,7 +102,7 @@ async function emitAction(action, objects, serviceTag) {
87
102
  flow: null,
88
103
  flowStepName: '',
89
104
  startConfig: null,
90
- objects: action === 'Create' ? [] : objects,
105
+ objects: objects,
91
106
  isCrudObject,
92
107
  hasRbacAddOn: (object) => hasRbacAddOn(object, objects),
93
108
  plan
@@ -3,7 +3,8 @@ import {
3
3
  CRUD_ACTIONS,
4
4
  createCrudActionPlans,
5
5
  hasGraphStorageResource,
6
- isCrudObject
6
+ isCrudObject,
7
+ VALID_CRUD_GENERATED_BY
7
8
  } from './crudActionPlan.js';
8
9
 
9
10
  const MESSAGE_ATTRIBUTES = ['userId', 'correlationId'];
@@ -13,24 +14,44 @@ export const dynamicCrudFlowSchemaPaths = CRUD_ACTIONS.map(
13
14
  `app/src/schemas/FlowSchemas/generateCode/FlowObjects/${action}.js`
14
15
  );
15
16
 
16
- export function emitDynamicCrudFlowSchemas({ objects, flows = [], serviceTag }) {
17
+ export function emitDynamicCrudFlowSchemas({
18
+ objects,
19
+ flows = [],
20
+ serviceTag,
21
+ logger
22
+ }) {
17
23
  if (!Array.isArray(objects) || !Array.isArray(flows)) {
18
24
  throw new TypeError('objects and flows must be arrays');
19
25
  }
20
- if (!objects.some(isCrudObject)) return [];
26
+ if (!objects.some(isCrudObject)) {
27
+ if (objects.length > 0) {
28
+ const summary = objects.map((obj) => ({
29
+ objectType: obj?.objectType ?? '(undefined)',
30
+ generatedBy: obj?.generatedBy ?? '(missing)'
31
+ }));
32
+ logger?.warn?.(
33
+ `[WARN] [CRUD FlowSchemas] Skipped FlowObjects FlowSchemas generation: No valid CRUD ObjectSchema found. ` +
34
+ `Checked ${objects.length} object(s): ${JSON.stringify(summary)}. ` +
35
+ `Expected "generatedBy" to be one of: ${Array.from(VALID_CRUD_GENERATED_BY).join(', ')}`
36
+ );
37
+ }
38
+ return [];
39
+ }
21
40
 
22
41
  const plans = createCrudActionPlans(objects);
42
+ const primaryObject = objects.find(isCrudObject);
43
+ const initiateBy = primaryObject?.generatedBy || 'user';
23
44
 
24
45
  return CRUD_ACTIONS.map((action, index) => ({
25
46
  path: dynamicCrudFlowSchemaPaths[index],
26
47
  content: renderDynamicCrudFlowSchema(
27
48
  action,
28
- _createDynamicFlow(action, plans[action], serviceTag)
49
+ _createDynamicFlow(action, plans[action], serviceTag, initiateBy)
29
50
  )
30
51
  }));
31
52
  }
32
53
 
33
- function _createDynamicFlow(action, plan, serviceTag = '') {
54
+ function _createDynamicFlow(action, plan, serviceTag = '', initiateBy = 'user') {
34
55
  const flowSteps = [
35
56
  {
36
57
  stepName: 'Start',
@@ -103,7 +124,7 @@ function _createDynamicFlow(action, plan, serviceTag = '') {
103
124
 
104
125
  return {
105
126
  flowTag: action,
106
- initiateBy: 'system',
127
+ initiateBy,
107
128
  handleObj: 'one',
108
129
  statusType: 'none',
109
130
  event: plan.outputTopic ? ['ownTopic'] : ['lambdaSyncInv', 'lambdaSyncApi'],
@@ -9,10 +9,11 @@ import {
9
9
 
10
10
  export async function generateCrudApplicationArtifacts(rootPath, options = {}) {
11
11
  const project = compileProject({ ...options, rootPath });
12
- const schemas = await loadLocalSchemaArrays(project);
12
+ const schemas = await loadLocalSchemaArrays(project, options);
13
13
  const artifacts = await emitCrudApplicationArtifacts({
14
14
  ...schemas,
15
- serviceTag: project.serviceTag
15
+ serviceTag: project.serviceTag,
16
+ logger: options.logger
16
17
  });
17
18
  const files = await commitArtifacts(
18
19
  buildGenerationPlan(project, artifacts, {
@@ -9,12 +9,13 @@ import { loadLocalSchemaArrays } from './loadLocalSchemaArrays.js';
9
9
 
10
10
  export async function generateDynamicCrudFlowSchemas(rootPath, options = {}) {
11
11
  const project = compileProject({ ...options, rootPath });
12
- const schemaInputs = await loadLocalSchemaArrays(project);
12
+ const schemaInputs = await loadLocalSchemaArrays(project, options);
13
13
  const plan = buildGenerationPlan(
14
14
  project,
15
15
  emitDynamicCrudFlowSchemas({
16
16
  ...schemaInputs,
17
- serviceTag: project.serviceTag
17
+ serviceTag: project.serviceTag,
18
+ logger: options.logger
18
19
  }),
19
20
  {
20
21
  managedPaths: dynamicCrudFlowSchemaPaths,
@@ -1,8 +1,8 @@
1
1
  import { loadLocalSchemaDirectory } from '../../compile/loadSchemas.js';
2
2
 
3
- export async function loadLocalSchemaArrays(project) {
3
+ export async function loadLocalSchemaArrays(project, options = {}) {
4
4
  return {
5
- objects: await loadLocalSchemaDirectory(project, 'ObjectSchemas'),
6
- flows: await loadLocalSchemaDirectory(project, 'FlowSchemas')
5
+ objects: await loadLocalSchemaDirectory(project, 'ObjectSchemas', options),
6
+ flows: await loadLocalSchemaDirectory(project, 'FlowSchemas', options)
7
7
  };
8
8
  }
@@ -52,7 +52,7 @@ export default async function CreateComplete(
52
52
  //(</additionalParams>)
53
53
  ) {
54
54
  try {
55
- _izContext.logger.debug('[CreateComplete] started', {
55
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateComplete] started`, {
56
56
  requestParams
57
57
  });
58
58
  const {
@@ -65,7 +65,7 @@ export default async function <%- flowTag %>(
65
65
  //(</additionalParams>)
66
66
  ) {
67
67
  try {
68
- _izContext.logger.debug('[<%- flowTag %>] createMain:', {
68
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] createMain:`, {
69
69
  requestParams,
70
70
  callingFlowConfig
71
71
  });
@@ -78,16 +78,42 @@ export default async function <%- flowTag %>(
78
78
  _izContext,
79
79
  objType
80
80
  );
81
- _izContext.logger.debug('objectSchema', objectSchema);
82
-
83
- // ── Resolve userId / targetId ─────────────────────────────────────────────
84
- let userId, targetId;
85
- if (objectSchema.generatedBy === 'userGenerated') {
86
- userId = _izContext.correlationIds.get(coreConsts.BASE_USER_ID);
87
- targetId = _izContext.correlationIds.get(coreConsts.TARGET_ID);
88
- if (!userId) errorsFound.push('Not have userId');
89
- if (objectSchema.hasOwnProperty('belongTo') && !targetId)
90
- errorsFound.push('not have targetId');
81
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchema:`, objectSchema);
82
+
83
+ // ── Resolve submittedByUserId / targetIdentifiers ─────────────────────────
84
+ let submittedByUserId, targetIdentifiers;
85
+ if (
86
+ objectSchema.generatedBy === 'user' ||
87
+ objectSchema.generatedBy === 'systemWithUser'
88
+ ) {
89
+ submittedByUserId =
90
+ requestParams.submittedByUserId ||
91
+ _izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
92
+
93
+ targetIdentifiers = requestParams.targetIdentifiers;
94
+
95
+ if (!targetIdentifiers && objectSchema.belongTo?.objectType === 'user' && submittedByUserId) {
96
+ targetIdentifiers = { submittedByUserId };
97
+ }
98
+
99
+ if (!submittedByUserId) errorsFound.push('Not have submittedByUserId');
100
+ if (objectSchema.hasOwnProperty('belongTo') && !targetIdentifiers)
101
+ errorsFound.push('not have targetIdentifiers');
102
+
103
+ requestParams.submittedByUserId = submittedByUserId;
104
+ if (targetIdentifiers) {
105
+ if (!targetIdentifiers.submittedByUserId && submittedByUserId) {
106
+ targetIdentifiers.submittedByUserId = submittedByUserId;
107
+ }
108
+ requestParams.targetIdentifiers = targetIdentifiers;
109
+ }
110
+
111
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Identity & Target Context Resolved:`, {
112
+ submittedByUserId: requestParams.submittedByUserId,
113
+ targetIdentifiers: requestParams.targetIdentifiers,
114
+ belongTo: objectSchema.belongTo,
115
+ generatedBy: objectSchema.generatedBy
116
+ });
91
117
  } else if (objectSchema.generatedBy === 'searchResultGenerated') {
92
118
  throw new NoRetryError(
93
119
  'error searchResultGenerated should not happen here'
@@ -103,14 +129,14 @@ export default async function <%- flowTag %>(
103
129
  _izContext,
104
130
  objectSchema
105
131
  );
106
- _izContext.logger.debug('objectSchemas', objectSchemas);
132
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] objectSchemas:`, objectSchemas);
107
133
 
108
134
  // ── Build createDataDetails ───────────────────────────────────────────────
109
135
  const createDataDetails = await createDataDetailsLib(
110
136
  _izContext,
111
137
  objectSchemas
112
138
  );
113
- _izContext.logger.debug('createDataDetails:', createDataDetails);
139
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] createDataDetails:`, createDataDetails);
114
140
 
115
141
  if (requestParams.relationships?.length > 0) {
116
142
  //(<beforeValidateRelationships>)
@@ -124,9 +150,35 @@ export default async function <%- flowTag %>(
124
150
  requestParams.relationships
125
151
  ))
126
152
  );
127
- _izContext.logger.debug('errorsFound', errorsFound);
153
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] errorsFound:`, errorsFound);
154
+
155
+ // ── Resolve fromRequiredOnCreate fields ──────────────────────────────────
156
+ if (objectSchemas.fieldNames) {
157
+ if (!requestParams.fieldNames) requestParams.fieldNames = {};
158
+ for (const [fieldName, val] of Object.entries(objectSchemas.fieldNames)) {
159
+ if (val.fromRequiredOnCreate && requestParams.fieldNames[fieldName] === undefined) {
160
+ const fromConfig = val.fromRequiredOnCreate;
161
+ const sourceField = fromConfig.fieldName || fieldName;
162
+ if (fromConfig.belongToObjType === true && targetIdentifiers) {
163
+ const targetValue =
164
+ targetIdentifiers[sourceField] !== undefined
165
+ ? targetIdentifiers[sourceField]
166
+ : Object.values(targetIdentifiers)[0];
167
+ requestParams.fieldNames[fieldName] = targetValue;
168
+ } else if (fromConfig.relType && requestParams.relationships?.length > 0) {
169
+ const matchedRel = requestParams.relationships.find(
170
+ (rel) =>
171
+ rel.relType?.serviceTag === fromConfig.relType.serviceTag &&
172
+ rel.relType?.relationshipTag === fromConfig.relType.relationshipTag
173
+ );
174
+ if (matchedRel?.targetIdentifiers?.[sourceField] !== undefined) {
175
+ requestParams.fieldNames[fieldName] = matchedRel.targetIdentifiers[sourceField];
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
128
181
 
129
- // ── Build field lists ─────────────────────────────────────────────────────
130
182
  const listOfRequiredOnCreate = [];
131
183
  const listOfOptionalOnCreate = [];
132
184
 
@@ -134,6 +186,7 @@ export default async function <%- flowTag %>(
134
186
  if (
135
187
  val.randomOnCreate === true ||
136
188
  val.requiredOnCreate === true ||
189
+ val.fromRequiredOnCreate ||
137
190
  val.hashOnCreate?.length
138
191
  ) {
139
192
  listOfRequiredOnCreate.push(key);
@@ -177,9 +230,9 @@ export default async function <%- flowTag %>(
177
230
  }
178
231
  }
179
232
 
180
- _izContext.logger.debug('requestParams:', requestParams);
233
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:`, requestParams);
181
234
  errorsFound.push(...errors);
182
- _izContext.logger.debug('errorsFound::', errorsFound);
235
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] errorsFound after identifiers:`, errorsFound);
183
236
 
184
237
  if (errorsFound.length > 0)
185
238
  return await <%- flowTag %>LogicalLib(
@@ -216,13 +269,13 @@ export default async function <%- flowTag %>(
216
269
  });
217
270
  }
218
271
  if (errorsFound.length > 0) {
219
- _izContext.logger.debug('HaveError::', errorsFound);
272
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] HaveError:`, errorsFound);
220
273
  break;
221
274
  }
222
275
  }
223
276
 
224
277
  <%_
225
- const rbacObjects = objects.filter(o => (o?.generatedBy === 'userGenerated' || o?.generatedBy === 'systemGenerated') && hasRbacAddOn(o));
278
+ const rbacObjects = objects.filter(o => isCrudObject(o) && hasRbacAddOn(o));
226
279
  const rbacObjectTypes = rbacObjects.map(o => o.objectType);
227
280
  const rbacServiceTagMap = {};
228
281
  for (const o of rbacObjects) {
@@ -265,12 +318,12 @@ export default async function <%- flowTag %>(
265
318
  serviceTag: <%- rbacServiceTags.length === 1 ? rbacServiceTags[0] : `{ ${Object.entries(rbacServiceTagMap).map(([k, v]) => `'${k}': ${v}`).join(', ')} }[requestParams.objectType] || process.env.iz_serviceTag` %>
266
319
  },
267
320
  requestParams: requestParams,
268
- nextFlowStepName: '<%- flowTag %>RbacCallback'
321
+ nextFlowStepName: '<%- plan?.hasRbacCallback ? `${flowTag}RbacCallback` : `${flowTag}Complete` %>'
269
322
  }
270
323
  );
271
324
 
272
325
  _izContext.logger.debug(
273
- '[lib:auth:checkRbacPermission] resultPermissionCheck:',
326
+ `[${process.env.iz_serviceTag}:<%- flowTag %>] checkRbacPermission result:`,
274
327
  resultPermissionCheck
275
328
  );
276
329
 
@@ -436,6 +489,7 @@ export default async function <%- flowTag %>(
436
489
  errorsFound
437
490
  );
438
491
  } catch (err) {
492
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>] error:`, err);
439
493
  throw err;
440
494
  }
441
495
  }
@@ -77,10 +77,11 @@ export async function <%- flowTag %>LogicalLib(
77
77
  phaseResults = {}
78
78
  ) {
79
79
  try {
80
- _izContext.logger.debug('[<%- flowTag %>LogicalLib] started', {
80
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] started`, {
81
81
  requestParams,
82
82
  callingFlowConfig,
83
- phaseResults
83
+ phaseResults,
84
+ targetIdentifiers: requestParams.targetIdentifiers
84
85
  });
85
86
 
86
87
  //(<beforeCreateMainLogical>)
@@ -93,14 +94,14 @@ export async function <%- flowTag %>LogicalLib(
93
94
  _izContext,
94
95
  objType
95
96
  );
96
- _izContext.logger.debug('objectSchema', objectSchema);
97
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchema:`, objectSchema);
97
98
 
98
99
  const objectSchemas =
99
100
  uploadUseCase.generateObjectSchemaForCombineFieldNames(
100
101
  _izContext,
101
102
  objectSchema
102
103
  );
103
- _izContext.logger.debug('objectSchemas', objectSchemas);
104
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] objectSchemas:`, objectSchemas);
104
105
 
105
106
  // ── Parent object ─────────────────────────────────────────────────────────
106
107
  let parentObject;
@@ -121,7 +122,7 @@ export async function <%- flowTag %>LogicalLib(
121
122
  _izContext,
122
123
  objectSchemas
123
124
  );
124
- _izContext.logger.debug('createDataDetails:', createDataDetails);
125
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] createDataDetails:`, createDataDetails);
125
126
 
126
127
  // ── Build objInstanceFull identifiers ─────────────────────────────────────
127
128
  const objInstanceFull = { identifiers: {}, fields: {} };
@@ -177,7 +178,7 @@ export async function <%- flowTag %>LogicalLib(
177
178
 
178
179
  // ── DynamoDB ──────────────────────────────────────────────────────────
179
180
  if (createDataDetail.storageType === consts.STORAGE_TYPES.dynamoDB) {
180
- _izContext.logger.debug('::::::DynamoDB::::::', {
181
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] DynamoDB:`, {
181
182
  storageTag,
182
183
  objInstanceFull
183
184
  });
@@ -256,6 +257,66 @@ export async function <%- flowTag %>LogicalLib(
256
257
  );
257
258
 
258
259
  objInstanceFullForDynamoDb.fields = {};
260
+
261
+ // ── Auto-provision owner TargetRole for Root Object with RBAC ─────
262
+ const hasRbacAddOn = Array.isArray(objectSchema?.addOnDataStructure) &&
263
+ objectSchema.addOnDataStructure.some((addOn) => addOn?.type === 'rbac');
264
+ const isRootRbacObject =
265
+ hasRbacAddOn &&
266
+ !objectSchema?.belongTo &&
267
+ objectSchema?.objectType !== 'user' &&
268
+ objectSchema?.generatedBy !== 'systemNoUser';
269
+
270
+ if (isRootRbacObject) {
271
+ try {
272
+ const submittedByUserId = requestParams.submittedByUserId || _izContext.correlationIds.get(coreConsts.SUBMITTED_BY_USER_ID);
273
+ if (submittedByUserId) {
274
+ const targetIdentKeys = Object.entries(identifiersForCreateData)
275
+ .map(([k, v]) => `${k}#${v}`)
276
+ .join('_');
277
+ const targetIdentifiersConcat = targetIdentKeys || (identifiersForCreateData[Object.keys(identifiersForCreateData)[0]] ? `${Object.keys(identifiersForCreateData)[0]}#${identifiersForCreateData[Object.keys(identifiersForCreateData)[0]]}` : '');
278
+
279
+ if (targetIdentifiersConcat) {
280
+ const roleIdKey = `${targetIdentifiersConcat}_owner`;
281
+ _izContext.logger.debug('[CreateLogicalLib] Auto-provisioning owner TargetRole:', {
282
+ identifiersConcat: targetIdentifiersConcat,
283
+ roleIdKey,
284
+ submittedByUserId,
285
+ roleName: 'owner'
286
+ });
287
+
288
+ await dynamodbSharedLib.putItem(
289
+ _izContext,
290
+ dynamodbSharedLib.tableName(_izContext, 'TargetRoles'),
291
+ {
292
+ identifiersConcat: targetIdentifiersConcat,
293
+ roleIdKey,
294
+ submittedByUserId,
295
+ roleName: 'owner',
296
+ assignedAt: new Date().toISOString()
297
+ }
298
+ ).catch(() => {});
299
+
300
+ await dynamodbSharedLib.transactWriteItems(_izContext, [
301
+ {
302
+ Put: {
303
+ TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
304
+ Item: { roleIdKey, submittedByUserId }
305
+ }
306
+ },
307
+ {
308
+ Put: {
309
+ TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
310
+ Item: { submittedByUserId, roleIdKey }
311
+ }
312
+ }
313
+ ]).catch(() => {});
314
+ }
315
+ }
316
+ } catch (roleError) {
317
+ _izContext.logger.warn('[CreateLogicalLib] Failed to auto-provision owner TargetRole:', { roleError });
318
+ }
319
+ }
259
320
  }
260
321
 
261
322
  //(<afterCreateRecordDynamo>)
@@ -267,7 +328,7 @@ export async function <%- flowTag %>LogicalLib(
267
328
  ) {
268
329
  //(<beforeCreateNode>)
269
330
  //(</beforeCreateNode>)
270
- _izContext.logger.debug('::::::Graph::::::', {
331
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] Graph:`, {
271
332
  storageTag,
272
333
  objInstanceFull
273
334
  });
@@ -435,6 +496,7 @@ export async function <%- flowTag %>LogicalLib(
435
496
  objType,
436
497
  objInstanceFull: objInstanceFullForGraph,
437
498
  relationships: requestParams.relationships || [],
499
+ targetIdentifiers: requestParams.targetIdentifiers,
438
500
  status: 'complete',
439
501
  errorsFound
440
502
  };
@@ -444,7 +506,7 @@ export async function <%- flowTag %>LogicalLib(
444
506
 
445
507
  // ── Handle errors ─────────────────────────────────────────────────────────
446
508
  if (errorsFound.length > 0) {
447
- _izContext.logger.debug('Case have errorsFound:::', errorsFound);
509
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] errorsFound:`, errorsFound);
448
510
 
449
511
  await asyncFlowSharedLib.clearAllAwaitingSteps(
450
512
  _izContext,
@@ -479,6 +541,7 @@ export async function <%- flowTag %>LogicalLib(
479
541
  objType,
480
542
  objInstanceFull,
481
543
  relationships: requestParams.relationships || [],
544
+ targetIdentifiers: requestParams.targetIdentifiers,
482
545
  status: 'error',
483
546
  errorsFound
484
547
  };
@@ -529,10 +592,12 @@ export async function <%- flowTag %>LogicalLib(
529
592
  fields: objInstanceFullForDynamoDb.fields
530
593
  },
531
594
  relationships: requestParams.relationships || [],
595
+ targetIdentifiers: requestParams.targetIdentifiers,
532
596
  status: 'complete',
533
597
  errorsFound
534
598
  };
535
599
  } catch (err) {
600
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:<%- flowTag %>LogicalLib] error:`, err);
536
601
  throw err;
537
602
  }
538
603
  }
@@ -52,7 +52,7 @@ export default async function DeleteComplete(
52
52
  //(</additionalParams>)
53
53
  ) {
54
54
  try {
55
- _izContext.logger.debug('[DeleteComplete] started', {
55
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:DeleteComplete] started`, {
56
56
  requestParams
57
57
  });
58
58
  const {