@izara_project/izara-core-generate-service-code 1.0.66 → 1.0.67

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 (27) hide show
  1. package/docs/TriggeredCache Flow.drawio +193 -0
  2. package/docs/test.mmd +47 -0
  3. package/docs/test.png +0 -0
  4. package/package.json +2 -2
  5. package/src/compile/loadSchemas.js +23 -1
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +9 -5
  7. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +5 -5
  8. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +5 -5
  9. package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +25 -4
  10. package/src/emit/crud/templates/FlowHandler_HdrApi.ejs +18 -0
  11. package/src/emit/crud/templates/GetEndpoint_Main.ejs +2 -2
  12. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +10 -8
  13. package/src/emit/custom/customFlows.js +76 -25
  14. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +92 -75
  15. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +4 -4
  16. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_Main.ejs +1 -1
  17. package/src/emit/function/functionYamlArtifact.js +26 -12
  18. package/src/emit/rbac/emitRbacApplicationArtifacts.js +21 -7
  19. package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +6 -5
  20. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +2 -2
  21. package/src/emit/resource/emitResourceArtifacts.js +400 -105
  22. package/src/emit/system/emitSystemWebSocketArtifacts.js +2 -1
  23. package/src/generate.js +2 -2
  24. package/test/crudApplicationArtifacts.test.js +1 -1
  25. package/test/customFlowApplicationArtifacts.test.js +37 -34
  26. package/test/publicApi.test.js +9 -9
  27. package/test/resourceArtifacts.test.js +1 -1
package/src/generate.js CHANGED
@@ -46,7 +46,7 @@ async function emitGroups({ project, objects, relationships, flows }) {
46
46
  emitCrudApplicationArtifacts({ objects, flows, serviceTag: project.serviceTag }),
47
47
  emitRelationshipApplicationArtifacts({ relationships, flows, serviceTag: project.serviceTag }),
48
48
  emitRbacApplicationArtifacts(objects),
49
- emitSystemWebSocketArtifacts(flows, { includeRelationshipOwnTopic: relationships.length > 0 }),
49
+ emitSystemWebSocketArtifacts(flows, { includeRelationshipOwnTopic: true }),
50
50
  emitResourceArtifacts({ objects, relationships, flows })
51
51
  ]);
52
52
  const custom = await emitCustomFlowApplicationArtifacts(flows, { systemFunctions: system.functions, objects });
@@ -58,7 +58,7 @@ async function emitGroups({ project, objects, relationships, flows }) {
58
58
  { scope: 'relationship-application', managedPaths: relationshipApplicationManagedPaths(), artifacts: relationship },
59
59
  { scope: 'rbac-application', managedPaths: rbacApplicationManagedPaths(objects), artifacts: rbac },
60
60
  { scope: 'system-websocket', managedPaths: systemWebSocketManagedPaths(), artifacts: system.artifacts },
61
- { scope: 'custom-flow-application', managedPaths: customFlowApplicationManagedPaths(flows, { includeYaml: hasSystemWebSocket(flows, { includeRelationshipOwnTopic: relationships.length > 0 }) }), artifacts: custom },
61
+ { scope: 'custom-flow-application', managedPaths: customFlowApplicationManagedPaths(flows, { includeYaml: hasSystemWebSocket(flows, { includeRelationshipOwnTopic: true }) }), artifacts: custom },
62
62
  { scope: 'shared-resources', managedPaths: resourceArtifactPaths, artifacts: resource }
63
63
  ];
64
64
  }
@@ -14,7 +14,7 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
14
14
  const rootPath = await createProject(true);
15
15
  const result = await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
16
16
 
17
- assert.equal(result.files.length, 24);
17
+ assert.equal(result.files.length, 26);
18
18
  assert.ok(result.files.every(file => !file.path.includes('/Process')));
19
19
  assert.match(
20
20
  await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Get/source/Get_Main.js'), 'utf8'),
@@ -25,13 +25,13 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
25
25
  const result = await generateCustomFlowApplicationArtifacts(rootPath);
26
26
  await generateResourceArtifacts(rootPath);
27
27
 
28
- assert.equal(result.files.length, 40);
29
- assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Ext/source/ProcessExt_HdrSqs.js'), 'utf8'), /dummyId/);
30
- assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Api/source/ProcessApi_HdrApi.js'), 'utf8'), /ProcessApi/);
28
+ assert.equal(result.files.length, 38);
29
+ assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Ext/source/Ext_HdrSqs.js'), 'utf8'), /dummyId/);
30
+ assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Api/source/Api_HdrApi.js'), 'utf8'), /Api/);
31
31
  const yaml = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8');
32
- assert.match(yaml, /ProcessEventHdrInv:/);
32
+ assert.match(yaml, /EventHdrInv:/);
33
33
  assert.match(yaml, /rate\(1 minute\)/);
34
- assert.match(yaml, /ProcessExtHdrSqs:/);
34
+ assert.match(yaml, /ExtHdrSqs:/);
35
35
  const iam = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'), 'utf8');
36
36
  assert.match(iam, /EventRole:/);
37
37
  assert.match(iam, /ExtRole:/);
@@ -50,13 +50,11 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
50
50
  /"CurrentOrderTable"/
51
51
  );
52
52
  await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Own/source/ProcessOwn_Main.js')));
53
- assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Mixed/source/Mixed_HdrSqs.js'), 'utf8'), /Mixed_Main/);
54
- assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Mixed/source/ProcessMixed_HdrSqs.js'), 'utf8'), /ProcessMixed/);
53
+ assert.match(await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Mixed/source/Mixed_HdrSqs.js'), 'utf8'), /Mixed/);
55
54
  assert.match(yaml, /OwnHdrWbs:/);
56
55
  assert.match(yaml, /OwnHdrSqs:/);
57
56
  assert.ok(yaml.indexOf('OwnHdrWbs:') < yaml.indexOf('OwnHdrSqs:'));
58
57
  assert.match(yaml, /MixedHdrSqs:/);
59
- assert.match(yaml, /ProcessMixedHdrSqs:/);
60
58
  assert.match(yaml, /UploadProcessAfterUploadHdrS3:/);
61
59
  assert.match(
62
60
  await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Upload/source/UploadProcessAfterUpload_Main.js'), 'utf8'),
@@ -111,10 +109,10 @@ test('emits handlers, function YAML, and IAM roles for supported custom flow eve
111
109
  test('removes only V2-owned custom-flow application artifacts when supported events are removed', async () => {
112
110
  const rootPath = await createProject();
113
111
  await generateCustomFlowApplicationArtifacts(rootPath);
114
- await writeFile(join(rootPath, 'app/src/schemas/FlowSchemas/Flows.js'), "export default [{ flowTag: 'later', event: ['dsq'] }];\n");
112
+ await writeFile(join(rootPath, 'app/src/schemas/FlowSchemas/Flows.js'), "export default [{ flowTag: 'later', event: ['unsupported'] }];\n");
115
113
 
116
114
  assert.deepEqual((await generateCustomFlowApplicationArtifacts(rootPath)).files, []);
117
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Ext/source/ProcessExt_HdrSqs.js')));
115
+ await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Ext/source/Ext_HdrSqs.js')));
118
116
  await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Own/source/Own_HdrWbs.js')));
119
117
  await assert.rejects(readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml')));
120
118
  });
@@ -126,6 +124,7 @@ test('emits and removes ext-topic handlers declared by custom flow steps', async
126
124
 
127
125
  assert.ok(result.files.some(file => file.path === 'app/src/generatedCode/FlowSchemas/Stepped/source/SteppedComplete_HdrSqs.js'));
128
126
  assert.ok(result.files.some(file => file.path === 'app/src/generatedCode/FlowSchemas/Generated/source/GeneratedComplete_HdrSqs.js'));
127
+ assert.ok(result.files.some(file => file.path === 'app/src/generatedCode/FlowSchemas/DsqStep/source/DsqStepRetryDsq_HdrDsq.js'));
129
128
  assert.match(
130
129
  await readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Stepped/source/SteppedComplete_Main.js'), 'utf8'),
131
130
  /identifierUuid/
@@ -134,10 +133,18 @@ test('emits and removes ext-topic handlers declared by custom flow steps', async
134
133
  await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8'),
135
134
  /SteppedCompleteHdrSqs:\n handler: src\/generatedCode\/FlowSchemas\/Stepped\/source\/SteppedComplete_HdrSqs.main/
136
135
  );
136
+ assert.match(
137
+ await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8'),
138
+ /DsqStepRetryDsqHdrDsq:\n handler: src\/generatedCode\/FlowSchemas\/DsqStep\/source\/DsqStepRetryDsq_HdrDsq.main/
139
+ );
137
140
  assert.match(
138
141
  await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'),
139
142
  /^ SteppedCompleteHdrSqs:/m
140
143
  );
144
+ assert.match(
145
+ await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'),
146
+ /^ DsqStepRetryDsqHdrDsq:/m
147
+ );
141
148
  assert.match(
142
149
  await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'), 'utf8'),
143
150
  /GeneratedRole:/
@@ -163,13 +170,10 @@ test('emits and removes the legacy trigger-cache artifact suite', async () => {
163
170
 
164
171
  const source = 'app/src/generatedCode/FlowSchemas/Cart/source';
165
172
  for (const fileName of [
166
- 'ProcessTriggerCacheCart_Main.js',
167
- 'ProcessTriggerCacheCart_HdrDsq.js',
168
- 'CheckTriggerCacheCartComplete_Main.js',
169
- 'CheckTriggerCacheCartComplete_HdrSqs.js',
170
- 'TriggerCacheCartComplete_Main.js',
171
- 'TriggerCacheCartComplete_HdrSqs.js',
172
- 'TriggerCacheCartComplete_HdrDsq.js'
173
+ 'CartCheckTriggerCacheCartComplete_Main.js',
174
+ 'CartCheckTriggerCacheCartComplete_HdrSqs.js',
175
+ 'CartTriggerCacheCartComplete_Main.js',
176
+ 'CartTriggerCacheCartComplete_HdrSqs.js'
173
177
  ]) {
174
178
  const path = join(rootPath, source, fileName);
175
179
  await access(path);
@@ -178,38 +182,33 @@ test('emits and removes the legacy trigger-cache artifact suite', async () => {
178
182
 
179
183
  const flowYaml = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8');
180
184
  for (const functionName of [
181
- 'ProcessTriggerCacheCartHdrDsq',
182
- 'CheckTriggerCacheCartCompleteHdrSqs',
183
- 'TriggerCacheCartCompleteHdrSqs',
184
- 'TriggerCacheCartCompleteHdrDsq'
185
+ 'CartCheckTriggerCacheCartCompleteHdrSqs',
186
+ 'CartTriggerCacheCartCompleteHdrSqs'
185
187
  ]) {
186
188
  assert.match(flowYaml, new RegExp(`^${functionName}:`, 'm'));
187
189
  }
188
190
  assert.match(
189
- await readFile(join(rootPath, `${source}/CheckTriggerCacheCartComplete_HdrSqs.js`), 'utf8'),
191
+ await readFile(join(rootPath, `${source}/CartCheckTriggerCacheCartComplete_HdrSqs.js`), 'utf8'),
190
192
  /record\.body\.Message\.identifiers/
191
193
  );
192
194
 
193
195
  const resources = await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8');
194
- assert.match(resources, /^ OrderCartMain:/m);
196
+ assert.match(resources, /^ CartHdrSqs:/m);
195
197
  for (const queueName of [
196
- 'OrderCart_Main',
197
- 'CheckTriggerCacheCartCompleteHdrSqs',
198
- 'TriggerCacheCartCompleteHdrSqs',
199
- 'TriggerCacheCartCompleteHdrDsq'
198
+ 'CartHdrSqs',
199
+ 'CartCheckTriggerCacheCartCompleteHdrSqs',
200
+ 'CartTriggerCacheCartCompleteHdrSqs',
201
+ 'CartTriggerCacheCartCompleteHdrDsq'
200
202
  ]) assert.match(resources, new RegExp('QueueName: \\$\\{self:custom\\.iz_resourcePrefix\\}' + queueName));
201
203
  const iam = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/generate-shared-resource.yml'), 'utf8');
202
- for (const queueName of ['OrderCart_Main', 'CheckTriggerCacheCartCompleteHdrSqs', 'TriggerCacheCartCompleteHdrSqs', 'TriggerCacheCartCompleteHdrDsq']) {
203
- assert.match(iam, new RegExp(`CartRole:[\\s\\S]*${queueName}`));
204
- }
205
204
 
206
205
  await writeFile(join(rootPath, 'app/src/schemas/FlowSchemas/Cart.js'), "export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'none' };\n");
207
206
  await generateCustomFlowApplicationArtifacts(rootPath);
208
207
  await generateResourceArtifacts(rootPath);
209
208
 
210
- await assert.rejects(readFile(join(rootPath, `${source}/ProcessTriggerCacheCart_Main.js`)));
211
- assert.doesNotMatch(await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8'), /ProcessTriggerCacheCartHdrDsq/);
212
- assert.doesNotMatch(await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'), /OrderCart_Main/);
209
+ await assert.rejects(readFile(join(rootPath, `${source}/CartCheckTriggerCacheCartComplete_Main.js`)));
210
+ assert.doesNotMatch(await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8'), /CartCheckTriggerCacheCartCompleteHdrSqs/);
211
+ assert.doesNotMatch(await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'), /CartTriggerCacheCartCompleteHdrDsq/);
213
212
  });
214
213
 
215
214
  test('rejects an EventBridge schedule without a deployable name and rate', () => {
@@ -278,6 +277,10 @@ async function createStepProject() {
278
277
  join(schemaRoot, 'Generated.js'),
279
278
  "export default { flowTag: 'generated', event: ['ownTopic'], generatedCodeConfig: { generatedCodeTag: 'create' }, flowSteps: [{ stepName: 'start' }, { stepName: 'Complete', event: ['extTopic'] }] };\n"
280
279
  );
280
+ await writeFile(
281
+ join(schemaRoot, 'DsqStep.js'),
282
+ "export default { flowTag: 'dsqStep', event: ['extTopic'], flowSteps: [{ stepName: 'start' }, { stepName: 'RetryDsq', event: ['dsq'] }] };\n"
283
+ );
281
284
  return rootPath;
282
285
  }
283
286
 
@@ -288,7 +291,7 @@ async function createTriggerCacheProject() {
288
291
  await mkdir(schemaRoot, { recursive: true });
289
292
  await writeFile(
290
293
  join(schemaRoot, 'Cart.js'),
291
- "export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'triggerCache', objType: { serviceTag: 'Order' } };\n"
294
+ "export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'triggerCache', objType: { serviceTag: 'Order' }, flowSteps: [{ stepName: 'start' }, { stepName: 'CheckTriggerCacheCartComplete', checkTriggerType: 'flowType', event: ['extTopic'] }, { stepName: 'TriggerCacheCartComplete', checkTriggerType: 'complete', event: ['extTopic', 'dsq'] }] };\n"
292
295
  );
293
296
  return rootPath;
294
297
  }
@@ -21,7 +21,7 @@ test('generate commits schemas, application artifacts, and resources in one publ
21
21
  const rootPath = await createCrudProject();
22
22
  const result = await generate(rootPath);
23
23
 
24
- assert.equal(result.files.length, 36);
24
+ assert.strictEqual(result.files.length, 38);
25
25
  assert.equal(result.project.rootPath, rootPath);
26
26
  assert.match(
27
27
  await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'), 'utf8'),
@@ -104,17 +104,18 @@ test('generate commits the legacy trigger-cache suite with its queues', async ()
104
104
  const result = await generate(rootPath);
105
105
 
106
106
  for (const path of [
107
- 'app/src/generatedCode/FlowSchemas/Cart/source/ProcessTriggerCacheCart_Main.js',
108
- 'app/src/generatedCode/FlowSchemas/Cart/source/CheckTriggerCacheCartComplete_HdrSqs.js',
109
- 'app/src/generatedCode/FlowSchemas/Cart/source/TriggerCacheCartComplete_HdrDsq.js'
107
+ 'app/src/generatedCode/FlowSchemas/Cart/source/CartCheckTriggerCacheCartComplete_HdrSqs.js',
108
+ 'app/src/generatedCode/FlowSchemas/Cart/source/CartCheckTriggerCacheCartComplete_Main.js',
109
+ 'app/src/generatedCode/FlowSchemas/Cart/source/CartTriggerCacheCartComplete_HdrSqs.js',
110
+ 'app/src/generatedCode/FlowSchemas/Cart/source/CartTriggerCacheCartComplete_Main.js'
110
111
  ]) assert.ok(result.files.some(file => file.path === path));
111
112
  assert.match(
112
113
  await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml'), 'utf8'),
113
- /ProcessTriggerCacheCartHdrDsq:[\s\S]*TriggerCacheCartCompleteHdrDsq:/
114
+ /CartCheckTriggerCacheCartCompleteHdrSqs:[\s\S]*CartTriggerCacheCartCompleteHdrSqs:/
114
115
  );
115
116
  assert.match(
116
117
  await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'),
117
- /OrderCartMain:[\s\S]*TriggerCacheCartCompleteHdrDsq/
118
+ /CartCheckTriggerCacheCartCompleteHdrSqs:[\s\S]*CartTriggerCacheCartCompleteHdrSqs/
118
119
  );
119
120
  });
120
121
 
@@ -127,7 +128,6 @@ test('generate removes the system WebSocket path when the last own-topic flow is
127
128
 
128
129
  await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/SystemFlowSchemas/WebSocketMain/source/WebSocketComplete_HdrSqs.js')));
129
130
  await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowSchemas/Track/source/Track_HdrWbs.js')));
130
- await assert.rejects(readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-schema.yml')));
131
131
  assert.doesNotMatch(
132
132
  await readFile(join(rootPath, 'resource/sls_yaml/generatedCode/source/generated-sns-in-sqs.yml'), 'utf8'),
133
133
  /WebSocketCompleteHdrSqs/
@@ -192,7 +192,7 @@ async function createOwnTopicProject() {
192
192
  await mkdir(flowDirectory, { recursive: true });
193
193
  await writeFile(
194
194
  join(flowDirectory, 'Track.js'),
195
- "export default { flowTag: 'track', event: ['ownTopic'], outputTopic: true, statusType: 'none', generatedCodeConfig: { generatedCodeTag: 'create', codeHookTag: 'beforeLogical', invocationType: 'ownTopic' } };\n"
195
+ "export default { flowTag: 'track', event: ['ownTopic'], initiateBy: 'user', outputTopic: true, statusType: 'none', generatedCodeConfig: { generatedCodeTag: 'create', codeHookTag: 'beforeLogical', invocationType: 'ownTopic' } };\n"
196
196
  );
197
197
  return rootPath;
198
198
  }
@@ -203,7 +203,7 @@ async function createTriggerCacheProject() {
203
203
  await mkdir(flowDirectory, { recursive: true });
204
204
  await writeFile(
205
205
  join(flowDirectory, 'Cart.js'),
206
- "export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'triggerCache', objType: { serviceTag: 'Order' } };\n"
206
+ "export default { flowTag: 'cart', event: ['ownTopic'], statusType: 'triggerCache', objType: { serviceTag: 'Order' }, flowSteps: [{ stepName: 'CheckTriggerCacheCartComplete', event: ['extTopic'], checkTriggerType: 'flowType' }, { stepName: 'TriggerCacheCartComplete', event: ['extTopic'], checkTriggerType: 'complete' }] };\n"
207
207
  );
208
208
  return rootPath;
209
209
  }
@@ -84,7 +84,7 @@ test('emits the shared WebSocket completion queue and output-topic subscriptions
84
84
  for (const roleName of ['WebSocketMainRole', 'FlowObjectsRole', 'FlowRelationshipRole', 'FlowRbacRole', 'CustomRole', 'UploadRole']) {
85
85
  const role = roleBlock(iam, roleName);
86
86
  assert.match(role, /execute-api:ManageConnections/);
87
- assert.match(role, /arn:aws:execute-api:\$\{self:custom\.iz_region\}:\$\{self:custom\.iz_accountId\}:\$\{self:custom\.iz_webSocketHostId\}\/\*/);
87
+ assert.match(role, /arn:aws:execute-api:\$\{self:custom\.iz_region\}:\$\{self:custom\.iz_accountId\}:\*\/\*/);
88
88
  }
89
89
  assert.match(resources, /SubscriptionToOutCreateRelationshipWebSocketComplete:/);
90
90
  });