@kumori/aurora-backend-handler 1.0.0
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/.gitlab-ci.yml +57 -0
- package/README.md +352 -0
- package/api/account-api-service.ts +1092 -0
- package/api/deploy-service-helper.ts +1611 -0
- package/api/environment-api-service.ts +542 -0
- package/api/marketplace-api-service.ts +1031 -0
- package/api/organizations-api-service.ts +0 -0
- package/api/planProvider-api-service.ts +24 -0
- package/api/reporting-api-service.ts +35 -0
- package/api/resources-api-service.ts +821 -0
- package/api/service-api-service.ts +796 -0
- package/api/tenant-api-service.ts +1260 -0
- package/api/user-api-service.ts +1161 -0
- package/backend-handler.ts +1127 -0
- package/environment.ts +7 -0
- package/event-helper.ts +577 -0
- package/event-names.ts +152 -0
- package/helpers/account-helper.ts +331 -0
- package/helpers/environment-helper.ts +289 -0
- package/helpers/link-helper.ts +114 -0
- package/helpers/plan-helper.ts +104 -0
- package/helpers/registry-helper.ts +134 -0
- package/helpers/resource-helper.ts +387 -0
- package/helpers/revision-helper.ts +899 -0
- package/helpers/service-helper.ts +627 -0
- package/helpers/tenant-helper.ts +191 -0
- package/helpers/token-helper.ts +107 -0
- package/helpers/user-helper.ts +140 -0
- package/jest.config.ts +40 -0
- package/jest.setup.js +4 -0
- package/package.json +50 -0
- package/test/backend-handler.test.ts +792 -0
- package/test/deploy-service-helper.test.ts +518 -0
- package/test/event-helper.test.ts +152 -0
- package/tsconfig.json +26 -0
- package/utils/utils.ts +78 -0
- package/websocket-manager.ts +1833 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
|
|
2
|
+
import { Channel, Service, Tenant, Usage } from "@hestekumori/aurora-interfaces";
|
|
3
|
+
import { getTimestamp } from "../utils/utils";
|
|
4
|
+
|
|
5
|
+
interface Role {
|
|
6
|
+
name: string;
|
|
7
|
+
instances: any[];
|
|
8
|
+
logo?: string;
|
|
9
|
+
category?: string;
|
|
10
|
+
version?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
resource?: any[];
|
|
13
|
+
parameters?: { [key: string]: string }[];
|
|
14
|
+
registry?: string;
|
|
15
|
+
imageTag?: string;
|
|
16
|
+
entrypoint?: string;
|
|
17
|
+
cmd?: string;
|
|
18
|
+
scalling?: {
|
|
19
|
+
cpu: { up: string; down: string };
|
|
20
|
+
memory: { up: string; down: string };
|
|
21
|
+
instances: { max: number; min: number };
|
|
22
|
+
histeresys: string;
|
|
23
|
+
};
|
|
24
|
+
hsize?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Revision {
|
|
28
|
+
service: string;
|
|
29
|
+
revision: string;
|
|
30
|
+
usage: Usage;
|
|
31
|
+
status: {
|
|
32
|
+
code: string;
|
|
33
|
+
message: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
args: string[];
|
|
36
|
+
};
|
|
37
|
+
errorCode?: string;
|
|
38
|
+
errorMsg?: string;
|
|
39
|
+
createdAt?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface HandleServiceEventParams {
|
|
43
|
+
entityId: string;
|
|
44
|
+
eventData: any;
|
|
45
|
+
parentParts: { [entity: string]: string };
|
|
46
|
+
servicesMap: Map<string, Service>;
|
|
47
|
+
revisionsMap: Map<string, Revision>;
|
|
48
|
+
roleMap: Map<string, Role[]>;
|
|
49
|
+
tenantsMap: Map<string, Tenant>;
|
|
50
|
+
pendingRevisionErrors: Array<{ service: string; revision: Revision }>;
|
|
51
|
+
pendingProjects: Array<{ tenant: string; project: string }>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface HandleServiceEventResult {
|
|
55
|
+
service: Service;
|
|
56
|
+
serviceFullKey: string;
|
|
57
|
+
wasDeployed: boolean;
|
|
58
|
+
pendingProject: { tenant: string; project: string } | null;
|
|
59
|
+
updatedPendingRevisionErrors: Array<{ service: string; revision: Revision }>;
|
|
60
|
+
}
|
|
61
|
+
interface HandleServiceOperationSuccessParams {
|
|
62
|
+
action: string;
|
|
63
|
+
entityName: string;
|
|
64
|
+
originalData: any;
|
|
65
|
+
responsePayload: any;
|
|
66
|
+
servicesMap: Map<string, Service>;
|
|
67
|
+
revisionsMap: Map<string, Revision>;
|
|
68
|
+
roleMap: Map<string, Role[]>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface HandleServiceOperationSuccessResult {
|
|
72
|
+
updatedService: Service | null;
|
|
73
|
+
shouldDelete: boolean;
|
|
74
|
+
eventType: "deployed" | "updated" | "deleting" | null;
|
|
75
|
+
processRevisionData: boolean;
|
|
76
|
+
revisionData: any;
|
|
77
|
+
serviceId: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface HandleServiceOperationErrorParams {
|
|
81
|
+
action: string;
|
|
82
|
+
entityName: string;
|
|
83
|
+
originalData: any;
|
|
84
|
+
error: any;
|
|
85
|
+
servicesMap: Map<string, Service>;
|
|
86
|
+
roleMap: Map<string, Role[]>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface HandleServiceOperationErrorResult {
|
|
90
|
+
eventType:
|
|
91
|
+
| "deploymentError"
|
|
92
|
+
| "updateError"
|
|
93
|
+
| "deletionError"
|
|
94
|
+
| "revisionError"
|
|
95
|
+
| null;
|
|
96
|
+
serviceId: string;
|
|
97
|
+
shouldResetRoles: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Parse key path to extract entity names
|
|
102
|
+
*/
|
|
103
|
+
const parseKeyPath = (key: string): { [entity: string]: string } => {
|
|
104
|
+
if (!key) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
const cleanKey = key?.startsWith("/") ? key.slice(1) : key;
|
|
108
|
+
const parts = cleanKey.split("/");
|
|
109
|
+
const result: { [entity: string]: string } = {};
|
|
110
|
+
for (let i = 0; i < parts.length; i += 2) {
|
|
111
|
+
if (parts[i] && parts[i + 1]) {
|
|
112
|
+
const entityType = parts[i];
|
|
113
|
+
const entityName = parts[i + 1];
|
|
114
|
+
result[entityType] = entityName;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get default usage object
|
|
122
|
+
*/
|
|
123
|
+
const getDefaultUsage = (): Usage => ({
|
|
124
|
+
current: {
|
|
125
|
+
cpu: 0,
|
|
126
|
+
memory: 0,
|
|
127
|
+
storage: 0,
|
|
128
|
+
volatileStorage: 0,
|
|
129
|
+
nonReplicatedStorage: 0,
|
|
130
|
+
persistentStorage: 0,
|
|
131
|
+
},
|
|
132
|
+
limit: {
|
|
133
|
+
cpu: { max: 0, min: 0 },
|
|
134
|
+
memory: { max: 0, min: 0 },
|
|
135
|
+
storage: { max: 0, min: 0 },
|
|
136
|
+
volatileStorage: { max: 0, min: 0 },
|
|
137
|
+
nonReplicatedStorage: { max: 0, min: 0 },
|
|
138
|
+
persistentStorage: { max: 0, min: 0 },
|
|
139
|
+
},
|
|
140
|
+
cost: 0,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Collect revisions for a service from revisionsMap
|
|
145
|
+
*/
|
|
146
|
+
const collectServiceRevisions = (
|
|
147
|
+
entityId: string,
|
|
148
|
+
revisionsMap: Map<string, Revision>,
|
|
149
|
+
): string[] => {
|
|
150
|
+
const serviceRevisions: string[] = [];
|
|
151
|
+
revisionsMap.forEach((revision, key) => {
|
|
152
|
+
if (revision.service === entityId) {
|
|
153
|
+
serviceRevisions.push(revision.revision);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return serviceRevisions;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Determine final status and error based on timestamps
|
|
161
|
+
*/
|
|
162
|
+
const determineFinalStatusAndError = (
|
|
163
|
+
existingService: Service | undefined,
|
|
164
|
+
eventData: any,
|
|
165
|
+
pendingRevisionErrors: Array<{ service: string; revision: Revision }>,
|
|
166
|
+
entityId: string,
|
|
167
|
+
): {
|
|
168
|
+
finalStatus: any;
|
|
169
|
+
finalError: any;
|
|
170
|
+
pendingErrorIndex: number;
|
|
171
|
+
} => {
|
|
172
|
+
const incomingStatus = eventData.status.state;
|
|
173
|
+
const incomingTs = getTimestamp(incomingStatus.timestamp);
|
|
174
|
+
const currentTs = getTimestamp(existingService?.status?.timestamp);
|
|
175
|
+
|
|
176
|
+
const isNewer = !existingService || incomingTs > currentTs;
|
|
177
|
+
let finalStatus = existingService?.status;
|
|
178
|
+
let finalError = existingService?.error;
|
|
179
|
+
|
|
180
|
+
if (isNewer) {
|
|
181
|
+
finalStatus = incomingStatus;
|
|
182
|
+
|
|
183
|
+
if (eventData.status.error) {
|
|
184
|
+
finalError = eventData.status.error;
|
|
185
|
+
} else {
|
|
186
|
+
finalError = undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const pendingErrorIndex = pendingRevisionErrors.findIndex(
|
|
190
|
+
(pending) => pending.service === entityId,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (pendingErrorIndex !== -1) {
|
|
194
|
+
const pendingError = pendingRevisionErrors[pendingErrorIndex];
|
|
195
|
+
const pendingTs = getTimestamp(pendingError.revision.status.timestamp);
|
|
196
|
+
const currentDecisionTs = getTimestamp(finalStatus?.timestamp);
|
|
197
|
+
|
|
198
|
+
if (pendingTs > currentDecisionTs) {
|
|
199
|
+
finalStatus = pendingError.revision.status;
|
|
200
|
+
finalError = {
|
|
201
|
+
code: pendingError.revision.errorCode || "",
|
|
202
|
+
message: pendingError.revision.errorMsg || "",
|
|
203
|
+
timestamp: pendingError.revision.status.timestamp || "",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { finalStatus, finalError, pendingErrorIndex };
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Handles the "service" event from WebSocket messages
|
|
212
|
+
* Processes service data updates and manages project labels
|
|
213
|
+
*/
|
|
214
|
+
export const handleServiceEvent = ({
|
|
215
|
+
entityId,
|
|
216
|
+
eventData,
|
|
217
|
+
parentParts,
|
|
218
|
+
servicesMap,
|
|
219
|
+
revisionsMap,
|
|
220
|
+
roleMap,
|
|
221
|
+
tenantsMap,
|
|
222
|
+
pendingRevisionErrors,
|
|
223
|
+
pendingProjects,
|
|
224
|
+
}: HandleServiceEventParams): HandleServiceEventResult => {
|
|
225
|
+
const serviceFullKey = `${parentParts.tenant}/${entityId}`;
|
|
226
|
+
const serviceTenantId = parentParts.tenant;
|
|
227
|
+
const currentRevisionKey = `${serviceFullKey}-${eventData.status.revision}`;
|
|
228
|
+
const currentRevision = revisionsMap.get(currentRevisionKey);
|
|
229
|
+
|
|
230
|
+
const projectLabel = eventData.meta?.labels?.project;
|
|
231
|
+
const serviceRoles = roleMap.get(serviceFullKey) || [];
|
|
232
|
+
const serviceUsage = currentRevision?.usage || getDefaultUsage();
|
|
233
|
+
const serviceRevisions = collectServiceRevisions(entityId, revisionsMap);
|
|
234
|
+
const environmentPath = eventData.spec.environment;
|
|
235
|
+
const pathParts = parseKeyPath(environmentPath);
|
|
236
|
+
const existingService = servicesMap.get(serviceFullKey);
|
|
237
|
+
const { finalStatus, finalError, pendingErrorIndex } =
|
|
238
|
+
determineFinalStatusAndError(
|
|
239
|
+
existingService,
|
|
240
|
+
eventData,
|
|
241
|
+
pendingRevisionErrors,
|
|
242
|
+
entityId,
|
|
243
|
+
);
|
|
244
|
+
const updatedPendingRevisionErrors = [...pendingRevisionErrors];
|
|
245
|
+
if (pendingErrorIndex !== -1) {
|
|
246
|
+
updatedPendingRevisionErrors.splice(pendingErrorIndex, 1);
|
|
247
|
+
}
|
|
248
|
+
const baseServiceData = {
|
|
249
|
+
tenant: serviceTenantId,
|
|
250
|
+
account: eventData.spec.metadata.targetAccount || pathParts.account,
|
|
251
|
+
environment:
|
|
252
|
+
eventData.spec.metadata.targetEnvironment || pathParts.environment,
|
|
253
|
+
name: entityId,
|
|
254
|
+
revisions: [...serviceRevisions].sort((a, b) => Number(b) - Number(a)),
|
|
255
|
+
status: finalStatus || eventData.status.state,
|
|
256
|
+
error: finalError,
|
|
257
|
+
role: serviceRoles.length > 0 ? serviceRoles : existingService?.role || [],
|
|
258
|
+
usage: serviceUsage,
|
|
259
|
+
lastDeployed: eventData.spec.ctstamp,
|
|
260
|
+
project: projectLabel || existingService?.project || "",
|
|
261
|
+
currentRevision:
|
|
262
|
+
eventData.status.revision ||
|
|
263
|
+
(serviceRevisions.length > 0
|
|
264
|
+
? Math.max(...serviceRevisions.map(Number)).toString()
|
|
265
|
+
: ""),
|
|
266
|
+
startedAt: currentRevision?.createdAt || existingService?.startedAt || "",
|
|
267
|
+
};
|
|
268
|
+
let newService: Service;
|
|
269
|
+
|
|
270
|
+
if (existingService) {
|
|
271
|
+
newService = {
|
|
272
|
+
...existingService,
|
|
273
|
+
...baseServiceData,
|
|
274
|
+
};
|
|
275
|
+
} else {
|
|
276
|
+
newService = {
|
|
277
|
+
id: serviceFullKey,
|
|
278
|
+
logo: "",
|
|
279
|
+
description: "",
|
|
280
|
+
links: [],
|
|
281
|
+
resources: [],
|
|
282
|
+
parameters: [],
|
|
283
|
+
minReplicas: 0,
|
|
284
|
+
maxReplicas: 0,
|
|
285
|
+
registry: "",
|
|
286
|
+
imageName: "",
|
|
287
|
+
entrypoint: "",
|
|
288
|
+
cmd: "",
|
|
289
|
+
serverChannels: [],
|
|
290
|
+
clientChannels: [],
|
|
291
|
+
duplexChannels: [],
|
|
292
|
+
cloudProvider: "",
|
|
293
|
+
...baseServiceData,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const oldStatusCode = existingService?.status?.code;
|
|
297
|
+
const newStatusCode = newService.status.code;
|
|
298
|
+
|
|
299
|
+
const wasDeployed =
|
|
300
|
+
existingService !== undefined &&
|
|
301
|
+
oldStatusCode !== "SERVICE_READY" &&
|
|
302
|
+
newStatusCode === "SERVICE_READY" &&
|
|
303
|
+
eventData.status.deployed === true;
|
|
304
|
+
let pendingProject: { tenant: string; project: string } | null = null;
|
|
305
|
+
|
|
306
|
+
if (projectLabel) {
|
|
307
|
+
const tenant = tenantsMap.get(serviceTenantId);
|
|
308
|
+
if (!tenant) {
|
|
309
|
+
const existingPending = pendingProjects.find(
|
|
310
|
+
(pending) =>
|
|
311
|
+
pending.tenant === serviceTenantId &&
|
|
312
|
+
pending.project === projectLabel,
|
|
313
|
+
);
|
|
314
|
+
if (!existingPending) {
|
|
315
|
+
pendingProject = {
|
|
316
|
+
tenant: serviceTenantId,
|
|
317
|
+
project: projectLabel,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
service: newService,
|
|
325
|
+
serviceFullKey,
|
|
326
|
+
wasDeployed,
|
|
327
|
+
pendingProject,
|
|
328
|
+
updatedPendingRevisionErrors,
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Handles successful service operations
|
|
334
|
+
*/
|
|
335
|
+
export const handleServiceOperationSuccess = ({
|
|
336
|
+
action,
|
|
337
|
+
entityName,
|
|
338
|
+
originalData,
|
|
339
|
+
responsePayload,
|
|
340
|
+
servicesMap,
|
|
341
|
+
revisionsMap,
|
|
342
|
+
roleMap,
|
|
343
|
+
}: HandleServiceOperationSuccessParams): HandleServiceOperationSuccessResult => {
|
|
344
|
+
const serviceId = originalData
|
|
345
|
+
? `${originalData.tenant}/${entityName}`
|
|
346
|
+
: entityName;
|
|
347
|
+
|
|
348
|
+
if (action === "GET_CHANNELS") {
|
|
349
|
+
return {
|
|
350
|
+
updatedService: null,
|
|
351
|
+
shouldDelete: false,
|
|
352
|
+
eventType: null,
|
|
353
|
+
processRevisionData: false,
|
|
354
|
+
revisionData: null,
|
|
355
|
+
serviceId,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (action === "GET_REVISION") {
|
|
360
|
+
const revisionData = responsePayload?.data;
|
|
361
|
+
const service = servicesMap.get(serviceId);
|
|
362
|
+
|
|
363
|
+
if (!service) {
|
|
364
|
+
return {
|
|
365
|
+
updatedService: null,
|
|
366
|
+
shouldDelete: false,
|
|
367
|
+
eventType: null,
|
|
368
|
+
processRevisionData: false,
|
|
369
|
+
revisionData: null,
|
|
370
|
+
serviceId,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!revisionData?.solution) {
|
|
375
|
+
const resetService = {
|
|
376
|
+
...service,
|
|
377
|
+
role: [],
|
|
378
|
+
};
|
|
379
|
+
return {
|
|
380
|
+
updatedService: resetService,
|
|
381
|
+
shouldDelete: false,
|
|
382
|
+
eventType: null,
|
|
383
|
+
processRevisionData: false,
|
|
384
|
+
revisionData: null,
|
|
385
|
+
serviceId,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
updatedService: service,
|
|
390
|
+
shouldDelete: false,
|
|
391
|
+
eventType: null,
|
|
392
|
+
processRevisionData: true,
|
|
393
|
+
revisionData,
|
|
394
|
+
serviceId,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (action === "DELETE") {
|
|
399
|
+
const existingService = servicesMap.get(serviceId);
|
|
400
|
+
|
|
401
|
+
if (existingService) {
|
|
402
|
+
const updatedService = {
|
|
403
|
+
...existingService,
|
|
404
|
+
status: {
|
|
405
|
+
code: "REMOVING_SERVICE",
|
|
406
|
+
message: `Service ${entityName} is being removed`,
|
|
407
|
+
args: [],
|
|
408
|
+
timestamp: new Date().toISOString(),
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
return {
|
|
412
|
+
updatedService,
|
|
413
|
+
shouldDelete: false,
|
|
414
|
+
eventType: "deleting",
|
|
415
|
+
processRevisionData: false,
|
|
416
|
+
revisionData: null,
|
|
417
|
+
serviceId,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
updatedService: null,
|
|
423
|
+
shouldDelete: false,
|
|
424
|
+
eventType: null,
|
|
425
|
+
processRevisionData: false,
|
|
426
|
+
revisionData: null,
|
|
427
|
+
serviceId,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (originalData) {
|
|
431
|
+
const updatedService = {
|
|
432
|
+
...originalData,
|
|
433
|
+
status: {
|
|
434
|
+
code: "SERVICE_READY",
|
|
435
|
+
message: `Service (${entityName}) is ready.`,
|
|
436
|
+
args: [],
|
|
437
|
+
timestamp: new Date().toISOString(),
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
let eventType: "deployed" | "updated" | null = null;
|
|
442
|
+
if (action === "CREATE") {
|
|
443
|
+
eventType = "deployed";
|
|
444
|
+
} else if (action === "UPDATE") {
|
|
445
|
+
eventType = "updated";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
updatedService,
|
|
450
|
+
shouldDelete: false,
|
|
451
|
+
eventType,
|
|
452
|
+
processRevisionData: false,
|
|
453
|
+
revisionData: null,
|
|
454
|
+
serviceId,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
updatedService: null,
|
|
460
|
+
shouldDelete: false,
|
|
461
|
+
eventType: null,
|
|
462
|
+
processRevisionData: false,
|
|
463
|
+
revisionData: null,
|
|
464
|
+
serviceId,
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Handles failed service operations
|
|
470
|
+
*/
|
|
471
|
+
export const handleServiceOperationError = ({
|
|
472
|
+
action,
|
|
473
|
+
entityName,
|
|
474
|
+
originalData,
|
|
475
|
+
error,
|
|
476
|
+
servicesMap,
|
|
477
|
+
roleMap,
|
|
478
|
+
}: HandleServiceOperationErrorParams): HandleServiceOperationErrorResult => {
|
|
479
|
+
const serviceId = originalData
|
|
480
|
+
? `${originalData.tenant}/${entityName}`
|
|
481
|
+
: entityName;
|
|
482
|
+
|
|
483
|
+
if (action === "CREATE") {
|
|
484
|
+
return {
|
|
485
|
+
eventType: "deploymentError",
|
|
486
|
+
serviceId,
|
|
487
|
+
shouldResetRoles: false,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (action === "UPDATE") {
|
|
492
|
+
return {
|
|
493
|
+
eventType: "updateError",
|
|
494
|
+
serviceId,
|
|
495
|
+
shouldResetRoles: false,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (action === "DELETE") {
|
|
500
|
+
return {
|
|
501
|
+
eventType: "deletionError",
|
|
502
|
+
serviceId,
|
|
503
|
+
shouldResetRoles: false,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (action === "GET_REVISION") {
|
|
508
|
+
const service = servicesMap.get(serviceId);
|
|
509
|
+
if (service) {
|
|
510
|
+
return {
|
|
511
|
+
eventType: "revisionError",
|
|
512
|
+
serviceId,
|
|
513
|
+
shouldResetRoles: true,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
eventType: null,
|
|
520
|
+
serviceId,
|
|
521
|
+
shouldResetRoles: false,
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
export const mapChannelsFromApiData = (
|
|
525
|
+
apiData: any,
|
|
526
|
+
entityId: string,
|
|
527
|
+
): {
|
|
528
|
+
serverChannels: Channel[];
|
|
529
|
+
clientChannels: Channel[];
|
|
530
|
+
duplexChannels: Channel[];
|
|
531
|
+
} => {
|
|
532
|
+
const serverChannels: Channel[] = [];
|
|
533
|
+
const clientChannels: Channel[] = [];
|
|
534
|
+
const duplexChannels: Channel[] = [];
|
|
535
|
+
const inboundSuffix = "_inbound.inbound";
|
|
536
|
+
|
|
537
|
+
const publicChannelNames = new Set<string>();
|
|
538
|
+
|
|
539
|
+
if (apiData.server) {
|
|
540
|
+
Object.keys(apiData.server).forEach((channelName) => {
|
|
541
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
542
|
+
const baseName = channelName.slice(0, -inboundSuffix.length);
|
|
543
|
+
if (apiData.server[baseName]) {
|
|
544
|
+
publicChannelNames.add(baseName);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (apiData.client) {
|
|
551
|
+
Object.keys(apiData.client).forEach((channelName) => {
|
|
552
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
553
|
+
const baseName = channelName.slice(0, -inboundSuffix.length);
|
|
554
|
+
if (apiData.client[baseName]) {
|
|
555
|
+
publicChannelNames.add(baseName);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (apiData.duplex) {
|
|
562
|
+
Object.keys(apiData.duplex).forEach((channelName) => {
|
|
563
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
564
|
+
const baseName = channelName.slice(0, -inboundSuffix.length);
|
|
565
|
+
if (apiData.duplex[baseName]) {
|
|
566
|
+
publicChannelNames.add(baseName);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (apiData.server) {
|
|
572
|
+
Object.entries(apiData.server).forEach(
|
|
573
|
+
([channelName, channelData]: [string, any]) => {
|
|
574
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
serverChannels.push({
|
|
578
|
+
name: channelName,
|
|
579
|
+
from: entityId,
|
|
580
|
+
to: "",
|
|
581
|
+
protocol: channelData.protocol as "http" | "tcp" | "https",
|
|
582
|
+
port: channelData.port,
|
|
583
|
+
portNum: channelData.port,
|
|
584
|
+
isPublic: publicChannelNames.has(channelName),
|
|
585
|
+
});
|
|
586
|
+
},
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
if (apiData.client) {
|
|
590
|
+
Object.entries(apiData.client).forEach(
|
|
591
|
+
([channelName, channelData]: [string, any]) => {
|
|
592
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
clientChannels.push({
|
|
596
|
+
name: channelName,
|
|
597
|
+
from: entityId,
|
|
598
|
+
to: "",
|
|
599
|
+
protocol: channelData.protocol as "http" | "tcp" | "https",
|
|
600
|
+
port: channelData.port,
|
|
601
|
+
portNum: channelData.port,
|
|
602
|
+
isPublic: publicChannelNames.has(channelName),
|
|
603
|
+
});
|
|
604
|
+
},
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
if (apiData.duplex) {
|
|
608
|
+
Object.entries(apiData.duplex).forEach(
|
|
609
|
+
([channelName, channelData]: [string, any]) => {
|
|
610
|
+
if (channelName.endsWith(inboundSuffix)) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
duplexChannels.push({
|
|
614
|
+
name: channelName,
|
|
615
|
+
from: entityId,
|
|
616
|
+
to: "",
|
|
617
|
+
protocol: channelData.protocol as "http" | "tcp" | "https",
|
|
618
|
+
port: channelData.port,
|
|
619
|
+
portNum: channelData.port,
|
|
620
|
+
isPublic: publicChannelNames.has(channelName),
|
|
621
|
+
});
|
|
622
|
+
},
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return { serverChannels, clientChannels, duplexChannels };
|
|
627
|
+
};
|