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

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +5 -0
  3. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +13 -4
  4. package/src/emit/crud/crudActionPlan.js +3 -1
  5. package/src/emit/crud/emitCrudApplicationArtifacts.js +37 -4
  6. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +126 -21
  7. package/src/emit/crud/templates/CreateConfirmComplete_Main.ejs +54 -0
  8. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +248 -181
  9. package/src/emit/crud/templates/CreateLogicalLib.ejs +1155 -293
  10. package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +221 -0
  11. package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +20 -120
  12. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +76 -14
  13. package/src/emit/crud/templates/FlowMain_Wbs.ejs +31 -35
  14. package/src/emit/rbac/templates/CancelUsage_Main.ejs +82 -22
  15. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +130 -14
  16. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +29 -10
  17. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +30 -11
  18. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +28 -3
  19. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +10 -7
  20. package/src/emit/resource/emitResourceArtifacts.js +9 -0
  21. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +3 -1
  22. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +2 -0
  23. package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +1 -1
  24. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +0 -1
  25. package/test/dynamicCrudFlowSchemas.test.js +16 -2
  26. package/test/resourceArtifacts.test.js +21 -0
@@ -22,35 +22,68 @@ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
22
 
23
23
  export default async function CancelUsage(_izContext, requestParams) {
24
24
  try {
25
+ let runningTotalReservedId =
26
+ requestParams?.runningTotalReservedId ||
27
+ requestParams?.reservedId ||
28
+ requestParams?.runningTotalReservedUsageId;
29
+
30
+ const usedTimestampId =
31
+ requestParams?.usedTimestampId ||
32
+ requestParams?.uniqueIdentify;
33
+
25
34
  const submittedByUserId =
26
- requestParams.submittedByUserId ||
35
+ requestParams?.submittedByUserId ||
27
36
  _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
- const { limitTag, uniqueIdentify } = requestParams;
37
+ const flowTypeConcat =
38
+ requestParams?.flowTypeConcat ||
39
+ requestParams?.limitTag;
29
40
 
30
- const targetIdentifiers = requestParams.targetIdentifiers;
31
- let identifiersConcat = requestParams.identifiersConcat;
41
+ const targetIdentifiers = requestParams?.targetIdentifiers;
42
+ let identifiersConcat = requestParams?.identifiersConcat;
32
43
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
- identifiersConcat = Object.entries(targetIdentifiers)
34
- .map(([k, v]) => `${k}#${v}`)
35
- .join('_');
44
+ if (targetIdentifiers.identifierConcat) {
45
+ identifiersConcat = targetIdentifiers.identifierConcat;
46
+ } else {
47
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
48
+ const parts = [];
49
+ for (const key of sortedKeys) {
50
+ const value = targetIdentifiers[key];
51
+ if (
52
+ key !== 'submittedByUserId' &&
53
+ key !== 'identifierConcat' &&
54
+ value !== undefined &&
55
+ value !== null
56
+ ) {
57
+ parts.push(`${key}#${value}`);
58
+ }
59
+ }
60
+ if (parts.length > 0) {
61
+ identifiersConcat = parts.join('|');
62
+ }
63
+ }
36
64
  }
37
65
  if (!identifiersConcat) {
38
66
  identifiersConcat = submittedByUserId;
39
67
  }
40
68
 
41
- if (!identifiersConcat || !limitTag || !uniqueIdentify) {
69
+ if (!runningTotalReservedId) {
70
+ if (identifiersConcat && flowTypeConcat) {
71
+ runningTotalReservedId = `${identifiersConcat}_${flowTypeConcat}`;
72
+ }
73
+ }
74
+
75
+ if (!runningTotalReservedId || !usedTimestampId) {
42
76
  throw new NoRetryError(
43
- 'identifiersConcat, limitTag, and uniqueIdentify are required.'
77
+ 'runningTotalReservedId (or identifiersConcat + flowTypeConcat) and usedTimestampId (or uniqueIdentify) are required.'
44
78
  );
45
79
  }
46
80
 
47
- const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
48
81
  const reservedRecord = await dynamodbSharedLib.getItem(
49
82
  _izContext,
50
83
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
51
84
  {
52
85
  runningTotalReservedId,
53
- usedTimestampId: uniqueIdentify
86
+ usedTimestampId
54
87
  }
55
88
  );
56
89
 
@@ -61,36 +94,63 @@ export default async function CancelUsage(_izContext, requestParams) {
61
94
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
62
95
  {
63
96
  runningTotalReservedId,
64
- usedTimestampId: uniqueIdentify
97
+ usedTimestampId
65
98
  },
66
99
  {
67
100
  runningTotalUsageStatus: 'cancelled'
68
101
  }
69
102
  );
70
103
 
71
- if (usageCount > 0) {
104
+ if (usageCount > 0 && identifiersConcat && flowTypeConcat) {
72
105
  const usageRecord = await dynamodbSharedLib.getItem(
73
106
  _izContext,
74
107
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
75
- { identifiersConcat, limitTag }
108
+ { identifiersConcat, flowTypeConcat }
76
109
  );
77
110
  if (usageRecord) {
78
- const newTotal = Math.max(0, (usageRecord.totalCount || 0) - usageCount);
79
- await dynamodbSharedLib.putItem(
111
+ const currentTotal = usageRecord.totalCount || 0;
112
+ const newTotal = Math.max(0, currentTotal - usageCount);
113
+ const queryElementsWhenUpdate = {
114
+ additionalAttributes: { currentUuid: usageRecord.updateUuid },
115
+ logicalElements: [
116
+ {
117
+ type: 'logical',
118
+ comparison: 'equals',
119
+ lhsAttribute: 'updateUuid',
120
+ rhsReference: 'currentUuid'
121
+ }
122
+ ],
123
+ returnValues: 'ALL_NEW'
124
+ };
125
+
126
+ const updateAttributes = [
127
+ { attributeName: 'totalCount', action: 'set', value: newTotal },
128
+ { attributeName: 'updateUuid', action: 'set', value: _izContext.uniqueRequestId || `${Date.now()}` }
129
+ ];
130
+
131
+ await dynamodbSharedLib.updateItem(
80
132
  _izContext,
81
133
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
134
+ { identifiersConcat, flowTypeConcat },
135
+ updateAttributes,
136
+ queryElementsWhenUpdate,
82
137
  {
83
- identifiersConcat,
84
- limitTag,
85
- totalCount: newTotal,
86
- updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
138
+ complexAttributes: true,
139
+ errorOnConditionalExpNotPass: false
87
140
  }
88
- );
141
+ ).catch(() => null);
89
142
  }
90
143
  }
91
144
 
92
145
  await sns.publishMessage(_izContext, {
93
- message: { returnResult: { status: true, uniqueIdentify } },
146
+ message: {
147
+ returnResult: {
148
+ status: true,
149
+ uniqueIdentify: usedTimestampId,
150
+ usedTimestampId,
151
+ runningTotalReservedId
152
+ }
153
+ },
94
154
  TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
95
155
  _izContext,
96
156
  'CancelUsage_Out'
@@ -22,43 +22,159 @@ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
22
 
23
23
  export default async function ConfirmUsage(_izContext, requestParams) {
24
24
  try {
25
+ let runningTotalReservedId =
26
+ requestParams?.runningTotalReservedId ||
27
+ requestParams?.reservedId ||
28
+ requestParams?.runningTotalReservedUsageId;
29
+
30
+ const usedTimestampId =
31
+ requestParams?.usedTimestampId ||
32
+ requestParams?.uniqueIdentify;
33
+
25
34
  const submittedByUserId =
26
- requestParams.submittedByUserId ||
35
+ requestParams?.submittedByUserId ||
27
36
  _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
- const { limitTag, uniqueIdentify } = requestParams;
37
+ const flowTypeConcat =
38
+ requestParams?.flowTypeConcat ||
39
+ requestParams?.limitTag;
29
40
 
30
- const targetIdentifiers = requestParams.targetIdentifiers;
31
- let identifiersConcat = requestParams.identifiersConcat;
41
+ const targetIdentifiers = requestParams?.targetIdentifiers;
42
+ let identifiersConcat = requestParams?.identifiersConcat;
32
43
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
- identifiersConcat = Object.entries(targetIdentifiers)
34
- .map(([k, v]) => `${k}#${v}`)
35
- .join('_');
44
+ if (targetIdentifiers.identifierConcat) {
45
+ identifiersConcat = targetIdentifiers.identifierConcat;
46
+ } else {
47
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
48
+ const parts = [];
49
+ for (const key of sortedKeys) {
50
+ const value = targetIdentifiers[key];
51
+ if (
52
+ key !== 'submittedByUserId' &&
53
+ key !== 'identifierConcat' &&
54
+ value !== undefined &&
55
+ value !== null
56
+ ) {
57
+ parts.push(`${key}#${value}`);
58
+ }
59
+ }
60
+ if (parts.length > 0) {
61
+ identifiersConcat = parts.join('|');
62
+ }
63
+ }
36
64
  }
37
65
  if (!identifiersConcat) {
38
66
  identifiersConcat = submittedByUserId;
39
67
  }
40
68
 
41
- if (!identifiersConcat || !limitTag || !uniqueIdentify) {
69
+ if (!runningTotalReservedId) {
70
+ if (identifiersConcat && flowTypeConcat) {
71
+ runningTotalReservedId = `${identifiersConcat}_${flowTypeConcat}`;
72
+ }
73
+ }
74
+
75
+ if (!runningTotalReservedId || !usedTimestampId) {
42
76
  throw new NoRetryError(
43
- 'identifiersConcat, limitTag, and uniqueIdentify are required.'
77
+ 'runningTotalReservedId (or identifiersConcat + flowTypeConcat) and usedTimestampId (or uniqueIdentify) are required.'
44
78
  );
45
79
  }
46
80
 
47
- const runningTotalReservedId = `${identifiersConcat}_${limitTag}`;
81
+ const reservedTable = dynamodbSharedLib.tableName(
82
+ _izContext,
83
+ 'RunningTotalReservedUsage'
84
+ );
85
+
86
+ const reservedRecord = await dynamodbSharedLib
87
+ .getItem(_izContext, reservedTable, {
88
+ runningTotalReservedId,
89
+ usedTimestampId
90
+ })
91
+ .catch(() => null);
92
+
93
+ let usageCount = 1;
94
+ if (reservedRecord && typeof reservedRecord.usageCount === 'number') {
95
+ usageCount = reservedRecord.usageCount;
96
+ }
97
+
48
98
  await dynamodbSharedLib.updateItem(
49
99
  _izContext,
50
- dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
100
+ reservedTable,
51
101
  {
52
102
  runningTotalReservedId,
53
- usedTimestampId: uniqueIdentify
103
+ usedTimestampId
54
104
  },
55
105
  {
56
- runningTotalUsageStatus: 'confirmed'
106
+ runningTotalUsageStatus: 'confirmed',
107
+ confirmedAt: new Date().toISOString()
57
108
  }
58
109
  );
59
110
 
111
+ if (identifiersConcat && flowTypeConcat) {
112
+ const usageTable = dynamodbSharedLib.tableName(
113
+ _izContext,
114
+ 'RunningTotalUsage'
115
+ );
116
+
117
+ const usageRecord = await dynamodbSharedLib
118
+ .getItem(_izContext, usageTable, {
119
+ identifiersConcat,
120
+ flowTypeConcat
121
+ })
122
+ .catch(() => null);
123
+
124
+ let currentTotal = 0;
125
+ let queryElementsWhenUpdate = {};
126
+
127
+ if (usageRecord && typeof usageRecord.totalCount === 'number') {
128
+ currentTotal = usageRecord.totalCount;
129
+ queryElementsWhenUpdate = {
130
+ additionalAttributes: { currentUuid: usageRecord.updateUuid },
131
+ logicalElements: [
132
+ {
133
+ type: 'logical',
134
+ comparison: 'equals',
135
+ lhsAttribute: 'updateUuid',
136
+ rhsReference: 'currentUuid'
137
+ }
138
+ ],
139
+ returnValues: 'ALL_NEW'
140
+ };
141
+ } else {
142
+ queryElementsWhenUpdate = {
143
+ logicalElements: dynamodbSharedLib.generateNotExistLogicalElements({
144
+ identifiersConcat,
145
+ flowTypeConcat
146
+ }),
147
+ returnValues: 'ALL_NEW'
148
+ };
149
+ }
150
+
151
+ const updateAttributes = [
152
+ { attributeName: 'totalCount', action: 'set', value: currentTotal + usageCount },
153
+ { attributeName: 'updateUuid', action: 'set', value: _izContext.uniqueRequestId || `${Date.now()}` }
154
+ ];
155
+
156
+ await dynamodbSharedLib.updateItem(
157
+ _izContext,
158
+ usageTable,
159
+ { identifiersConcat, flowTypeConcat },
160
+ updateAttributes,
161
+ queryElementsWhenUpdate,
162
+ {
163
+ complexAttributes: true,
164
+ errorOnConditionalExpNotPass: false
165
+ }
166
+ ).catch(() => null);
167
+ }
168
+
60
169
  await sns.publishMessage(_izContext, {
61
- message: { returnResult: { status: true, uniqueIdentify } },
170
+ message: {
171
+ returnResult: {
172
+ status: true,
173
+ uniqueIdentify: usedTimestampId,
174
+ usedTimestampId,
175
+ runningTotalReservedId
176
+ }
177
+ },
62
178
  TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
63
179
  _izContext,
64
180
  'ConfirmUsage_Out'
@@ -30,9 +30,26 @@ export default async function CreateTargetRole(_izContext, requestParams) {
30
30
  const targetIdentifiers = requestParams.targetIdentifiers;
31
31
  let identifiersConcat = requestParams.identifiersConcat;
32
32
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
- identifiersConcat = Object.entries(targetIdentifiers)
34
- .map(([k, v]) => `${k}#${v}`)
35
- .join('_');
33
+ if (targetIdentifiers.identifierConcat) {
34
+ identifiersConcat = targetIdentifiers.identifierConcat;
35
+ } else {
36
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
37
+ const parts = [];
38
+ for (const key of sortedKeys) {
39
+ const value = targetIdentifiers[key];
40
+ if (
41
+ key !== 'submittedByUserId' &&
42
+ key !== 'identifierConcat' &&
43
+ value !== undefined &&
44
+ value !== null
45
+ ) {
46
+ parts.push(`${key}#${value}`);
47
+ }
48
+ }
49
+ if (parts.length > 0) {
50
+ identifiersConcat = parts.join('|');
51
+ }
52
+ }
36
53
  }
37
54
  if (!identifiersConcat) {
38
55
  identifiersConcat = submittedByUserId;
@@ -57,20 +74,22 @@ export default async function CreateTargetRole(_izContext, requestParams) {
57
74
  try {
58
75
  await dynamodbSharedLib.transactWriteItems(_izContext, [
59
76
  {
60
- Put: {
61
- TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
62
- Item: { roleIdKey, submittedByUserId }
77
+ action: 'put',
78
+ params: {
79
+ tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
80
+ attributes: { roleIdKey, submittedByUserId }
63
81
  }
64
82
  },
65
83
  {
66
- Put: {
67
- TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
68
- Item: { submittedByUserId, roleIdKey }
84
+ action: 'put',
85
+ params: {
86
+ tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
87
+ attributes: { submittedByUserId, roleIdKey }
69
88
  }
70
89
  }
71
90
  ]);
72
91
  } catch (assignError) {
73
- _izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateTargetRole] UserRoles transactWrite ignored:', assignError);
92
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:CreateTargetRole] UserRoles transactWrite ignored:`, assignError);
74
93
  }
75
94
  }
76
95
 
@@ -25,8 +25,10 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
25
25
  const submittedByUserId =
26
26
  requestParams.submittedByUserId ||
27
27
  _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+ const flowTypeConcat =
29
+ requestParams.flowTypeConcat ||
30
+ requestParams.limitTag;
28
31
  const {
29
- limitTag,
30
32
  value = 1,
31
33
  usedTimestamp = new Date().toISOString(),
32
34
  roleLimitId
@@ -35,9 +37,26 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
35
37
  const targetIdentifiers = requestParams.targetIdentifiers;
36
38
  let identifiersConcat = requestParams.identifiersConcat;
37
39
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
38
- identifiersConcat = Object.entries(targetIdentifiers)
39
- .map(([k, v]) => `${k}#${v}`)
40
- .join('_');
40
+ if (targetIdentifiers.identifierConcat) {
41
+ identifiersConcat = targetIdentifiers.identifierConcat;
42
+ } else {
43
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
44
+ const parts = [];
45
+ for (const key of sortedKeys) {
46
+ const value = targetIdentifiers[key];
47
+ if (
48
+ key !== 'submittedByUserId' &&
49
+ key !== 'identifierConcat' &&
50
+ value !== undefined &&
51
+ value !== null
52
+ ) {
53
+ parts.push(`${key}#${value}`);
54
+ }
55
+ }
56
+ if (parts.length > 0) {
57
+ identifiersConcat = parts.join('|');
58
+ }
59
+ }
41
60
  }
42
61
  if (!identifiersConcat) {
43
62
  identifiersConcat = submittedByUserId;
@@ -46,14 +65,14 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
46
65
  if (!identifiersConcat) {
47
66
  throw new NoRetryError('Identifier not found.');
48
67
  }
49
- if (!limitTag) {
50
- throw new NoRetryError('limitTag is required.');
68
+ if (!flowTypeConcat) {
69
+ throw new NoRetryError('flowTypeConcat (or limitTag) is required.');
51
70
  }
52
71
 
53
72
  const uniqueIdentify = `${usedTimestamp}_${_izContext.uniqueRequestId || Date.now()}`;
54
73
  const roleLimitKeys = Array.isArray(requestParams.roleLimitIds)
55
74
  ? requestParams.roleLimitIds
56
- : [roleLimitId || `${identifiersConcat}_${limitTag}`];
75
+ : [roleLimitId || `${identifiersConcat}_${flowTypeConcat}`];
57
76
 
58
77
  let maxLimit = Infinity;
59
78
  const limitRecords = [];
@@ -93,7 +112,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
93
112
  const usageRecord = await dynamodbSharedLib.getItem(
94
113
  _izContext,
95
114
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
96
- { identifiersConcat, limitTag }
115
+ { identifiersConcat, flowTypeConcat }
97
116
  );
98
117
  const currentTotal = usageRecord?.totalCount || 0;
99
118
 
@@ -106,7 +125,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
106
125
  _izContext,
107
126
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
108
127
  {
109
- runningTotalReservedId: `${identifiersConcat}_${limitTag}`,
128
+ runningTotalReservedId: `${identifiersConcat}_${flowTypeConcat}`,
110
129
  usedTimestampId: uniqueIdentify,
111
130
  usageCount: value,
112
131
  runningTotalUsageStatus: 'reserved',
@@ -119,7 +138,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
119
138
  dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
120
139
  {
121
140
  identifiersConcat,
122
- limitTag,
141
+ flowTypeConcat,
123
142
  totalCount: currentTotal + value,
124
143
  updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
125
144
  }
@@ -129,7 +148,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
129
148
  const returnResult = {
130
149
  passStatus,
131
150
  uniqueIdentify,
132
- limitTag,
151
+ flowTypeConcat,
133
152
  identifiersConcat
134
153
  };
135
154
 
@@ -92,12 +92,37 @@ async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
92
92
  export default async function <%- flowTag %>(_izContext, requestParams = {}) {
93
93
  const submittedByUserId =
94
94
  requestParams.submittedByUserId ||
95
+ requestParams.targetIdentifiers?.submittedByUserId ||
95
96
  _izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
96
97
 
97
98
  const targetIdentifiers = requestParams.targetIdentifiers;
98
- let targetId = targetIdentifiers && typeof targetIdentifiers === 'object'
99
- ? Object.entries(targetIdentifiers).map(([k, v]) => `${k}#${v}`).join('_')
100
- : undefined;
99
+ let targetId = '';
100
+ if (targetIdentifiers) {
101
+ if (typeof targetIdentifiers === 'string') {
102
+ targetId = targetIdentifiers;
103
+ } else if (typeof targetIdentifiers === 'object') {
104
+ if (targetIdentifiers.identifierConcat) {
105
+ targetId = targetIdentifiers.identifierConcat;
106
+ } else {
107
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
108
+ const parts = [];
109
+ for (const key of sortedKeys) {
110
+ const value = targetIdentifiers[key];
111
+ if (
112
+ key !== 'submittedByUserId' &&
113
+ key !== 'identifierConcat' &&
114
+ value !== undefined &&
115
+ value !== null
116
+ ) {
117
+ parts.push(`${key}#${value}`);
118
+ }
119
+ }
120
+ if (parts.length > 0) {
121
+ targetId = parts.join('|');
122
+ }
123
+ }
124
+ }
125
+ }
101
126
 
102
127
  if (!submittedByUserId || !targetIdentifiers) {
103
128
  throw new Error('Missing required request parameters: submittedByUserId or targetIdentifiers');
@@ -16,7 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
18
  <%
19
- const itemType = operation === 'Put' ? 'Item' : 'Key';
19
+ const action = operation === 'Put' ? 'put' : 'delete';
20
+ const propName = operation === 'Put' ? 'attributes' : 'keyValues';
20
21
  const roleUsersKey = operation === 'Put' ? 'userId, roleIdKey' : 'roleIdKey, userId';
21
22
  -%> import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
22
23
  import snsSharedLib from '@izara_project/izara-core-library-sns';
@@ -27,15 +28,17 @@ export default async function <%- flowTag %>(_izContext, requestParams) {
27
28
  const { roleIdKey, userId } = requestParams;
28
29
  await dynamodbSharedLib.transactWriteItems(_izContext, [
29
30
  {
30
- <%- operation %>: {
31
- TableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
32
- <%- itemType %>: { <%- roleUsersKey %> }
31
+ action: '<%- action %>',
32
+ params: {
33
+ tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
34
+ <%- propName %>: { <%- roleUsersKey %> }
33
35
  }
34
36
  },
35
37
  {
36
- <%- operation %>: {
37
- TableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
38
- <%- itemType %>: { userId, roleIdKey }
38
+ action: '<%- action %>',
39
+ params: {
40
+ tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
41
+ <%- propName %>: { userId, roleIdKey }
39
42
  }
40
43
  }
41
44
  ]);
@@ -215,6 +215,9 @@ function createResourceState({ objects, relationships, flows }) {
215
215
  if (crud) {
216
216
  for (const action of ['Create', 'Update', 'Delete']) {
217
217
  const plan = crudActionPlans[action];
218
+ if (plan.hasCreateNodeCallback) {
219
+ crudSqsQueues.push(`${action}NodeCallbackHdrSqs`);
220
+ }
218
221
  if (plan.hasComplete) {
219
222
  crudSqsQueues.push(`${action}CompleteHdrSqs`);
220
223
  }
@@ -474,6 +477,9 @@ function createResourceState({ objects, relationships, flows }) {
474
477
  flow.initiateBy === 'systemWithUser'))) &&
475
478
  upperFirst(flow.flowTag) === topic
476
479
  ) ||
480
+ (objects.length > 0 &&
481
+ topic !== 'Get' &&
482
+ CRUD_ACTIONS.includes(topic)) ||
477
483
  (relationships.length > 0 &&
478
484
  topic !== 'GetRelationship' &&
479
485
  RELATIONSHIP_ACTIONS.includes(topic))
@@ -832,6 +838,9 @@ function roleQueueNames(state, roleName) {
832
838
  if (!plan.outputTopic) return [];
833
839
  return [
834
840
  `${action}HdrSqs`,
841
+ ...(plan.hasCreateNodeCallback
842
+ ? [`${action}NodeCallbackHdrSqs`]
843
+ : []),
835
844
  ...(plan.hasRbacCallback
836
845
  ? [`${action}RbacCallbackHdrSqs`]
837
846
  : []),
@@ -56,7 +56,9 @@ export function generatedLambdaRole() {
56
56
  Effect: 'Allow',
57
57
  Action: ['sns:Publish'],
58
58
  Resource: [
59
- `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
59
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', 'Rbac')}`,
60
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ConfirmUsage_In', 'Rbac')}`,
61
+ `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'CancelUsage_In', 'Rbac')}`<% (rbacValidatorServiceTags || []).forEach(function(tag) { %>,
60
62
  `arn:aws:sns:${process.env.iz_region}:${process.env.iz_accountId}:${snsSharedLib.snsTopicNameByFlowSchema(_izContext, 'ValidatorRbac_In', '<%= tag %>')}`<% }); %>
61
63
  ]
62
64
  }<% } %>
@@ -271,6 +271,8 @@ Resources:
271
271
  TimeToLiveSpecification:
272
272
  AttributeName: ttlTimestamp
273
273
  Enabled: true
274
+ StreamSpecification:
275
+ StreamViewType: NEW_AND_OLD_IMAGES
274
276
  <% } %>
275
277
  <% if (hasS3) { %>
276
278
  ReservedDataMainTable:
@@ -88,7 +88,7 @@ export const main = middleware.wrap(async (event, context) => {
88
88
 
89
89
  // add argument (to invoke lambda) to passOnProperties[]
90
90
  passOnProperties.push(record.body.Message);
91
- <% if (typeof usePassBack === 'undefined' ? functionName.endsWith('Complete') : usePassBack) { %>
91
+ <% if (typeof usePassBack === 'undefined' ? (functionName.endsWith('Complete') || functionName.endsWith('CompleteMain') || functionName.includes('Callback')) : usePassBack) { %>
92
92
  passOnProperties.push(
93
93
  callingFlowSharedLib.addPassBackPropertiesToPassOnProperties(
94
94
  record.body.Message
@@ -9,7 +9,6 @@ WebSocketConnect:
9
9
  authorizer:
10
10
  arn: arn:aws:lambda:${self:custom.iz_region}:${self:custom.iz_accountId}:function:UserAccount${self:custom.iz_stage}UserIdentityAuthorizer
11
11
  identitySource:
12
- - 'route.request.header.Authorization'
13
12
  - 'route.request.querystring.access_token'
14
13
  #<#/WebSocketConnectAuthorizer#>
15
14
  - websocket:
@@ -161,6 +161,7 @@ test('generates RbacCallback and Complete for Create when RBAC and Graph are pre
161
161
  assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
162
162
  'Start',
163
163
  'RbacCallback',
164
+ 'CreateNodeCallback',
164
165
  'Complete'
165
166
  ]);
166
167
  });
@@ -186,6 +187,7 @@ test('uses the renamed RBAC callback step when Graph storage is present', async
186
187
  assert.deepEqual(create.flowSteps.map((step) => step.stepName), [
187
188
  'Start',
188
189
  'RbacCallback',
190
+ 'CreateNodeCallback',
189
191
  'Complete'
190
192
  ]);
191
193
  assert.deepEqual(create.flowSteps[2].properties, [
@@ -221,10 +223,22 @@ test('chains RbacCallback and Complete for Update with RBAC and Graph', async ()
221
223
  'Complete'
222
224
  ]);
223
225
  assert.deepEqual(update.flowSteps[0].nextSteps, [
224
- { targetType: 'ownTopic', stepName: 'RbacCallback' }
226
+ { targetType: 'ownTopic', stepName: 'RbacCallback' },
227
+ {
228
+ targetType: 'extTopic',
229
+ serviceTag: 'Rbac',
230
+ topicName: 'ValidatorRbac_In',
231
+ toStepNames: ['RbacCallback']
232
+ }
225
233
  ]);
226
234
  assert.deepEqual(update.flowSteps[1].nextSteps, [
227
- { targetType: 'ownTopic', stepName: 'Complete' }
235
+ { targetType: 'ownTopic', stepName: 'Complete' },
236
+ {
237
+ targetType: 'extTopic',
238
+ serviceTag: 'GraphHandler',
239
+ topicName: 'UpdateNode_In',
240
+ toStepNames: ['Complete']
241
+ }
228
242
  ]);
229
243
  assert.deepEqual(update.flowSteps[2].properties, [
230
244
  'returnValue',