@kumori/aurora-backend-handler 1.0.100 → 1.0.112
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 +21 -21
- package/CHANGELOG.md +10 -0
- package/api/account-api-service.ts +35 -27
- package/api/deploy-service-helper.ts +72 -33
- package/api/environment-api-service.ts +20 -15
- package/api/marketplace-api-service.ts +32 -81
- package/api/resources-api-service.ts +28 -28
- package/api/service-api-service.ts +26 -22
- package/api/tenant-api-service.ts +145 -257
- package/api/user-api-service.ts +27 -12
- package/backend-handler.ts +373 -348
- package/developer.md +998 -0
- package/event-helper.ts +13 -3
- package/event-names.ts +3 -0
- package/helpers/account-helper.ts +15 -15
- package/helpers/environment-helper.ts +27 -30
- package/helpers/marketplace-helper.ts +109 -0
- package/helpers/resource-helper.ts +19 -1
- package/helpers/revision-helper.ts +68 -18
- package/helpers/service-helper.ts +49 -33
- package/helpers/tenant-helper.ts +0 -5
- package/helpers/token-helper.ts +74 -2
- package/helpers/user-helper.ts +71 -31
- package/jest.config.ts +1 -0
- package/package.json +3 -3
- package/test/backend-handler.test.ts +992 -730
- package/test/deploy-service-helper.test.ts +707 -273
- package/test/event-helper.test.ts +489 -104
- package/test/event-names.test.ts +186 -0
- package/websocket-manager.ts +369 -66
package/websocket-manager.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { v7 as uuidv7 } from "uuid";
|
|
2
2
|
import { environment } from "./environment";
|
|
3
|
+
import { AccountStatus } from "@kumori/aurora-interfaces/enums/accountStatusEnum";
|
|
3
4
|
import {
|
|
4
5
|
Account,
|
|
5
6
|
Channel,
|
|
6
7
|
ClusterToken,
|
|
7
8
|
Environment,
|
|
8
9
|
Instance,
|
|
9
|
-
|
|
10
|
-
MarketplaceService,
|
|
10
|
+
MarketplaceItem,
|
|
11
11
|
Notification,
|
|
12
12
|
Organization,
|
|
13
13
|
Plan,
|
|
@@ -17,11 +17,10 @@ import {
|
|
|
17
17
|
Resource,
|
|
18
18
|
Service,
|
|
19
19
|
Tenant,
|
|
20
|
-
tenantRole,
|
|
21
20
|
Token,
|
|
22
|
-
Usage,
|
|
23
21
|
User,
|
|
24
22
|
UserData,
|
|
23
|
+
UserStatus
|
|
25
24
|
} from "@kumori/aurora-interfaces";
|
|
26
25
|
import { loadMarketplaceItemsForTenant } from "./api/marketplace-api-service";
|
|
27
26
|
import { getReporting } from "./api/reporting-api-service";
|
|
@@ -31,8 +30,8 @@ import {
|
|
|
31
30
|
handleTenantOperationSuccess,
|
|
32
31
|
} from "./helpers/tenant-helper";
|
|
33
32
|
import {
|
|
33
|
+
handleUserDelete,
|
|
34
34
|
handleUserEvent,
|
|
35
|
-
handleUserOperationError,
|
|
36
35
|
} from "./helpers/user-helper";
|
|
37
36
|
import {
|
|
38
37
|
handleEnvironmentEvent,
|
|
@@ -63,6 +62,7 @@ import {
|
|
|
63
62
|
handleSecretEvent,
|
|
64
63
|
handleVolumeEvent,
|
|
65
64
|
processResourceResult,
|
|
65
|
+
setResourceDeleting,
|
|
66
66
|
} from "./helpers/resource-helper";
|
|
67
67
|
import {
|
|
68
68
|
handlePlanInstanceEvent,
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
} from "./helpers/plan-helper";
|
|
71
71
|
import { handleRegistryEvent } from "./helpers/registry-helper";
|
|
72
72
|
import {
|
|
73
|
+
handleTokenDeletion,
|
|
73
74
|
handleTokenEvent,
|
|
74
75
|
handleTokenOperationSuccess,
|
|
75
76
|
} from "./helpers/token-helper";
|
|
@@ -77,6 +78,7 @@ import { handleLinkEvent } from "./helpers/link-helper";
|
|
|
77
78
|
import { eventHelper } from "./backend-handler";
|
|
78
79
|
import { Revision } from "@kumori/aurora-interfaces/interfaces/revision-interface";
|
|
79
80
|
import { requestRevisionData } from "./api/service-api-service";
|
|
81
|
+
import { processMarketplaceSchemaResponse } from "./helpers/marketplace-helper";
|
|
80
82
|
|
|
81
83
|
let WebSocketClass: any;
|
|
82
84
|
|
|
@@ -145,10 +147,12 @@ interface Role {
|
|
|
145
147
|
*/
|
|
146
148
|
let wsConnection: WebSocket | null = null;
|
|
147
149
|
let pendingRequests = new Map<string, PendingOperation>();
|
|
150
|
+
let purgingEnvironments = new Set<string>();
|
|
148
151
|
let currentToken: string | null = null;
|
|
149
152
|
let connectionPromise: Promise<WebSocket> | null = null;
|
|
150
153
|
let userData: UserData = {
|
|
151
154
|
id: "",
|
|
155
|
+
created: "",
|
|
152
156
|
name: "",
|
|
153
157
|
surname: "",
|
|
154
158
|
provider: [],
|
|
@@ -171,6 +175,7 @@ let accountsMap = new Map<string, Account>();
|
|
|
171
175
|
let revisionsMap = new Map<string, Revision>();
|
|
172
176
|
let roleMap = new Map<string, Role[]>();
|
|
173
177
|
let tokenMap = new Map<string, Token>();
|
|
178
|
+
let deletedTokenIds = new Set<string>();
|
|
174
179
|
let platformInfo: Platform | null = null;
|
|
175
180
|
let isPlatformInfoLoading = false;
|
|
176
181
|
let isPlatformInfoLoaded = false;
|
|
@@ -192,6 +197,19 @@ const REPORTING_ITERATIONS = 5;
|
|
|
192
197
|
const REPORTING_INTERVAL = 1000;
|
|
193
198
|
let hasLoadedReportingOnce = false;
|
|
194
199
|
let recentlyUpdatedServices = new Map<string, Service>();
|
|
200
|
+
let pendingTenantStatus = new Map<string, string>();
|
|
201
|
+
let reconnectAttempts = 0;
|
|
202
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
203
|
+
let isManualClose = false;
|
|
204
|
+
let lastConnectionParams: {
|
|
205
|
+
token: string;
|
|
206
|
+
type?: string;
|
|
207
|
+
name?: string;
|
|
208
|
+
previousData?: UserData;
|
|
209
|
+
} | null = null;
|
|
210
|
+
const MAX_RECONNECT_ATTEMPTS = 5;
|
|
211
|
+
const RECONNECT_BASE_DELAY = 1000;
|
|
212
|
+
const RECONNECT_MAX_DELAY = 30000;
|
|
195
213
|
/**
|
|
196
214
|
* Helper function to safely stringify error objects
|
|
197
215
|
*/
|
|
@@ -234,6 +252,7 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
234
252
|
): Promise<WebSocket> => {
|
|
235
253
|
userData.notifications = previousData?.notifications;
|
|
236
254
|
globalEntityName = name || null;
|
|
255
|
+
lastConnectionParams = { token, type, name, previousData };
|
|
237
256
|
if (
|
|
238
257
|
wsConnection &&
|
|
239
258
|
wsConnection.readyState === WebSocket.OPEN &&
|
|
@@ -265,8 +284,9 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
265
284
|
if (typeof window === "undefined") {
|
|
266
285
|
wsConnection = new WebSocketClass(wsUrl, {
|
|
267
286
|
headers: {
|
|
268
|
-
authorization: `Bearer ${token}`,
|
|
287
|
+
// authorization: `Bearer ${token}`,
|
|
269
288
|
},
|
|
289
|
+
credentials: "include",
|
|
270
290
|
});
|
|
271
291
|
} else {
|
|
272
292
|
wsConnection = new WebSocketClass(wsUrl);
|
|
@@ -275,6 +295,11 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
275
295
|
wsConnection?.addEventListener("open", async () => {
|
|
276
296
|
currentToken = token;
|
|
277
297
|
connectionPromise = null;
|
|
298
|
+
reconnectAttempts = 0;
|
|
299
|
+
if (reconnectTimer) {
|
|
300
|
+
clearTimeout(reconnectTimer);
|
|
301
|
+
reconnectTimer = null;
|
|
302
|
+
}
|
|
278
303
|
|
|
279
304
|
resolve(wsConnection!);
|
|
280
305
|
});
|
|
@@ -346,7 +371,7 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
346
371
|
message.error?.code === "_unknown_user_" &&
|
|
347
372
|
message.error?.content !== "Unknown proposed user."
|
|
348
373
|
) {
|
|
349
|
-
userData.status =
|
|
374
|
+
userData.status = UserStatus.UNKNOWN;
|
|
350
375
|
user = new User(userData);
|
|
351
376
|
eventHelper.user.publish.loaded(user);
|
|
352
377
|
}
|
|
@@ -368,13 +393,17 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
368
393
|
pending.reject(new Error("WebSocket connection closed"));
|
|
369
394
|
pendingRequests.delete(messageId);
|
|
370
395
|
});
|
|
396
|
+
|
|
397
|
+
if (isManualClose) {
|
|
398
|
+
isManualClose = false;
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
scheduleReconnect();
|
|
371
403
|
});
|
|
372
404
|
|
|
373
405
|
wsConnection?.addEventListener("error", (error) => {
|
|
374
406
|
console.error("Global WebSocket error:", error);
|
|
375
|
-
userData.status = "notlogged";
|
|
376
|
-
user = new User(userData);
|
|
377
|
-
eventHelper.user.publish.loaded(user);
|
|
378
407
|
connectionPromise = null;
|
|
379
408
|
reject(error);
|
|
380
409
|
});
|
|
@@ -387,6 +416,38 @@ export const initializeGlobalWebSocketClient = async (
|
|
|
387
416
|
return connectionPromise;
|
|
388
417
|
};
|
|
389
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Schedule a reconnection attempt after an unexpected connection drop
|
|
421
|
+
* (e.g. a transient network blip while the tab was backgrounded).
|
|
422
|
+
* Uses exponential backoff and gives up after MAX_RECONNECT_ATTEMPTS.
|
|
423
|
+
*/
|
|
424
|
+
const scheduleReconnect = () => {
|
|
425
|
+
if (reconnectTimer || !lastConnectionParams) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
430
|
+
console.error("WebSocket reconnection attempts exhausted");
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const delay = Math.min(
|
|
435
|
+
RECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts),
|
|
436
|
+
RECONNECT_MAX_DELAY,
|
|
437
|
+
);
|
|
438
|
+
reconnectAttempts++;
|
|
439
|
+
|
|
440
|
+
reconnectTimer = setTimeout(() => {
|
|
441
|
+
reconnectTimer = null;
|
|
442
|
+
const { token, type, name, previousData } = lastConnectionParams!;
|
|
443
|
+
initializeGlobalWebSocketClient(token, type, name, previousData).catch(
|
|
444
|
+
(error) => {
|
|
445
|
+
console.error("WebSocket reconnect attempt failed:", error);
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
}, delay);
|
|
449
|
+
};
|
|
450
|
+
|
|
390
451
|
/**
|
|
391
452
|
* Function to make WebSocket requests using the global connection
|
|
392
453
|
* @param topic Request topic (group:method)
|
|
@@ -426,6 +487,14 @@ export const makeGlobalWebSocketRequest = async (
|
|
|
426
487
|
payload,
|
|
427
488
|
};
|
|
428
489
|
|
|
490
|
+
let purgeEnvKey: string | null = null;
|
|
491
|
+
if (petitionAction === "CLEAN" && entityType === "environment" && originalData) {
|
|
492
|
+
purgeEnvKey = originalData.tenant && originalData.account
|
|
493
|
+
? `${originalData.tenant}/${originalData.account}/${petitionInfo}`
|
|
494
|
+
: petitionInfo;
|
|
495
|
+
purgingEnvironments.add(purgeEnvKey);
|
|
496
|
+
}
|
|
497
|
+
|
|
429
498
|
return new Promise((resolve, reject) => {
|
|
430
499
|
const timeoutId = setTimeout(() => {
|
|
431
500
|
const pending = pendingRequests.get(messageId);
|
|
@@ -441,16 +510,23 @@ export const makeGlobalWebSocketRequest = async (
|
|
|
441
510
|
new Error(`Request timeout for ${topic}`),
|
|
442
511
|
);
|
|
443
512
|
}
|
|
444
|
-
|
|
513
|
+
if (purgeEnvKey) purgingEnvironments.delete(purgeEnvKey);
|
|
514
|
+
const timeoutError = Object.assign(
|
|
515
|
+
new Error(`Request timeout for ${topic}`),
|
|
516
|
+
{ isTimeout: true },
|
|
517
|
+
);
|
|
518
|
+
reject(timeoutError);
|
|
445
519
|
}, timeout);
|
|
446
520
|
|
|
447
521
|
const operation: PendingOperation = {
|
|
448
522
|
resolve: (response: any) => {
|
|
449
523
|
clearTimeout(timeoutId);
|
|
524
|
+
if (purgeEnvKey) purgingEnvironments.delete(purgeEnvKey);
|
|
450
525
|
resolve(response.payload || response);
|
|
451
526
|
},
|
|
452
527
|
reject: (error: any) => {
|
|
453
528
|
clearTimeout(timeoutId);
|
|
529
|
+
if (purgeEnvKey) purgingEnvironments.delete(purgeEnvKey);
|
|
454
530
|
const errorMessage = safeStringifyError(error);
|
|
455
531
|
console.error(`WebSocket request failed for ${topic}:`, {
|
|
456
532
|
originalError: error,
|
|
@@ -527,6 +603,23 @@ export const makeGlobalWebSocketRequestWithRetry = async (
|
|
|
527
603
|
);
|
|
528
604
|
};
|
|
529
605
|
|
|
606
|
+
export const markEnvironmentAsPurging = (envKey: string) => {
|
|
607
|
+
purgingEnvironments.add(envKey);
|
|
608
|
+
const env = environmentsMap.get(envKey);
|
|
609
|
+
if (env) {
|
|
610
|
+
const updatedEnv: Environment = {
|
|
611
|
+
...env,
|
|
612
|
+
status: {
|
|
613
|
+
code: "PURGING",
|
|
614
|
+
message: `Environment ${env.name} is being purged.`,
|
|
615
|
+
timestamp: new Date().toISOString(),
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
environmentsMap.set(envKey, updatedEnv);
|
|
619
|
+
eventHelper.environment.publish.updated(updatedEnv);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
530
623
|
/**
|
|
531
624
|
* Get the current WebSocket connection status
|
|
532
625
|
* @returns Connection status and token info
|
|
@@ -545,6 +638,14 @@ export const getWebSocketStatus = () => {
|
|
|
545
638
|
* Close the global WebSocket connection
|
|
546
639
|
*/
|
|
547
640
|
export const closeGlobalWebSocketConnection = () => {
|
|
641
|
+
isManualClose = true;
|
|
642
|
+
lastConnectionParams = null;
|
|
643
|
+
reconnectAttempts = 0;
|
|
644
|
+
if (reconnectTimer) {
|
|
645
|
+
clearTimeout(reconnectTimer);
|
|
646
|
+
reconnectTimer = null;
|
|
647
|
+
}
|
|
648
|
+
|
|
548
649
|
if (wsConnection) {
|
|
549
650
|
wsConnection.close();
|
|
550
651
|
wsConnection = null;
|
|
@@ -562,13 +663,24 @@ export const closeGlobalWebSocketConnection = () => {
|
|
|
562
663
|
});
|
|
563
664
|
};
|
|
564
665
|
|
|
666
|
+
const computePromotionInfo = (
|
|
667
|
+
tenants: Tenant[],
|
|
668
|
+
): { completedSteps: number; remainingDays: number } => {
|
|
669
|
+
const hasValidCredentialsAccount = tenants.some((tenant) =>
|
|
670
|
+
tenant.accounts?.some((account) => account.status === AccountStatus.ACTIVE),
|
|
671
|
+
);
|
|
672
|
+
const hasDeployedService = tenants.some((tenant) => tenant.services?.length > 0);
|
|
673
|
+
const completedSteps = (hasValidCredentialsAccount ? 1 : 0) + (hasDeployedService ? 1 : 0);
|
|
674
|
+
return { completedSteps, remainingDays: 7 };
|
|
675
|
+
};
|
|
676
|
+
|
|
565
677
|
const rebuildHierarchy = () => {
|
|
566
678
|
const preservedUserData = { ...userData };
|
|
567
679
|
|
|
568
680
|
tenantsMap.forEach((tenant) => {
|
|
569
681
|
tenant.accounts = Array.from(accountsMap.values()).filter((account) => {
|
|
570
682
|
account.environments = Array.from(environmentsMap.values())
|
|
571
|
-
.filter((env) => env.account === account.id)
|
|
683
|
+
.filter((env) => env.account === account.id && env.tenant === account.tenant)
|
|
572
684
|
.map((env) => env.name);
|
|
573
685
|
return account.tenant === tenant.id;
|
|
574
686
|
});
|
|
@@ -594,16 +706,20 @@ const rebuildHierarchy = () => {
|
|
|
594
706
|
}
|
|
595
707
|
}
|
|
596
708
|
});
|
|
597
|
-
|
|
598
709
|
userData = {
|
|
599
710
|
id: preservedUserData.id,
|
|
711
|
+
created: preservedUserData.created,
|
|
600
712
|
name: preservedUserData.name,
|
|
601
713
|
surname: preservedUserData.surname,
|
|
602
714
|
provider: preservedUserData.provider.map((p) => ({ ...p })),
|
|
603
715
|
notificationsEnabled: preservedUserData.notificationsEnabled,
|
|
604
716
|
organizations: Array.from(organizationsMap.values()),
|
|
605
717
|
clusterTokens: Array.from(clusterTokensMap.values()),
|
|
606
|
-
tokens:
|
|
718
|
+
tokens: (
|
|
719
|
+
tokenMap.size > 0
|
|
720
|
+
? (preservedUserData.tokens || []).filter(t => tokenMap.has(t.name))
|
|
721
|
+
: (preservedUserData.tokens || [])
|
|
722
|
+
).filter(t => !deletedTokenIds.has(t.name)),
|
|
607
723
|
tenants: Array.from(tenantsMap.values()),
|
|
608
724
|
axebowPlan: preservedUserData.axebowPlan,
|
|
609
725
|
companyName: preservedUserData.companyName,
|
|
@@ -613,6 +729,7 @@ const rebuildHierarchy = () => {
|
|
|
613
729
|
notifications: preservedUserData.notifications || [],
|
|
614
730
|
plans: preservedUserData.plans || [],
|
|
615
731
|
discoveryMethod: preservedUserData.discoveryMethod,
|
|
732
|
+
promotionInfo: computePromotionInfo(Array.from(tenantsMap.values())),
|
|
616
733
|
};
|
|
617
734
|
};
|
|
618
735
|
const handleEvent = async (message: WSMessage) => {
|
|
@@ -630,6 +747,7 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
630
747
|
: {};
|
|
631
748
|
switch (kind) {
|
|
632
749
|
case "user":
|
|
750
|
+
const previousTokens = userData.tokens || [];
|
|
633
751
|
const userEventResult = handleUserEvent({
|
|
634
752
|
eventData,
|
|
635
753
|
tenantsMap,
|
|
@@ -639,7 +757,22 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
639
757
|
parseKeyPath,
|
|
640
758
|
eventHelper,
|
|
641
759
|
});
|
|
760
|
+
Object.entries(eventData.status.tenants).forEach(
|
|
761
|
+
([tenantId, tenantInfo]: [string, any]) => {
|
|
762
|
+
if (tenantInfo.status === "rejected") {
|
|
763
|
+
const tenant = tenantsMap.get(tenantId);
|
|
764
|
+
if (tenant) {
|
|
765
|
+
tenantsMap.set(tenantId, { ...tenant, status: "leaving" });
|
|
766
|
+
} else {
|
|
767
|
+
pendingTenantStatus.set(tenantId, "leaving");
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
);
|
|
642
772
|
userData = userEventResult.userData;
|
|
773
|
+
if (!userData.tokens?.length && previousTokens.length) {
|
|
774
|
+
userData.tokens = previousTokens;
|
|
775
|
+
}
|
|
643
776
|
tempProviders = userEventResult.tempProviders;
|
|
644
777
|
userEventResult.deletedTenantKeys.forEach((key) => {
|
|
645
778
|
tenantsMap.delete(key);
|
|
@@ -660,7 +793,19 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
660
793
|
envEventResult.pendingCloudProviderUpdate,
|
|
661
794
|
);
|
|
662
795
|
}
|
|
663
|
-
|
|
796
|
+
const envSetKey = parentParts.tenant && parentParts.account ? `${parentParts.tenant}/${parentParts.account}/${entityId}` : entityId;
|
|
797
|
+
let envToStore = envEventResult.environment;
|
|
798
|
+
if (envToStore.status?.code?.startsWith("DELETING") && purgingEnvironments.has(envSetKey)) {
|
|
799
|
+
envToStore = {
|
|
800
|
+
...envToStore,
|
|
801
|
+
status: {
|
|
802
|
+
code: "PURGING",
|
|
803
|
+
message: `Environment ${entityId} is being purged.`,
|
|
804
|
+
timestamp: new Date().toISOString(),
|
|
805
|
+
},
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
environmentsMap.set(envSetKey, envToStore);
|
|
664
809
|
const envTenant = tenantsMap.get(envEventResult.tenantId);
|
|
665
810
|
if (!envTenant) {
|
|
666
811
|
pendingEnvironments.push({
|
|
@@ -694,12 +839,16 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
694
839
|
}
|
|
695
840
|
if (revisionResult.updatedEnvironment) {
|
|
696
841
|
const envId = revisionResult.updatedService?.environment;
|
|
697
|
-
|
|
698
|
-
|
|
842
|
+
const envRevTenant = revisionResult.updatedEnvironment.tenant;
|
|
843
|
+
const envRevAccount = revisionResult.updatedEnvironment.account;
|
|
844
|
+
const envRevKey = envRevTenant && envRevAccount && envId ? `${envRevTenant}/${envRevAccount}/${envId}` : envId;
|
|
845
|
+
if (envRevKey) environmentsMap.set(envRevKey, revisionResult.updatedEnvironment);
|
|
699
846
|
}
|
|
700
847
|
if (revisionResult.updatedAccount) {
|
|
701
848
|
const accId = revisionResult.updatedService?.account;
|
|
702
|
-
|
|
849
|
+
const accRevTenant = revisionResult.updatedAccount.tenant;
|
|
850
|
+
const accRevKey = accRevTenant && accId ? `${accRevTenant}/${accId}` : accId;
|
|
851
|
+
if (accRevKey) accountsMap.set(accRevKey, revisionResult.updatedAccount);
|
|
703
852
|
}
|
|
704
853
|
if (revisionResult.pendingRevisionError) {
|
|
705
854
|
pendingRevisionErrors.push(revisionResult.pendingRevisionError);
|
|
@@ -792,8 +941,9 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
792
941
|
});
|
|
793
942
|
|
|
794
943
|
if (!message.payload.deleted) {
|
|
795
|
-
|
|
796
|
-
|
|
944
|
+
const accountKey = parentParts.tenant ? `${parentParts.tenant}/${entityId}` : entityId;
|
|
945
|
+
accountsMap.set(accountKey, accountResult.account);
|
|
946
|
+
updateEnvironmentsCloudProvider(entityId, eventData.spec.api, parentParts.tenant);
|
|
797
947
|
}
|
|
798
948
|
break;
|
|
799
949
|
|
|
@@ -804,7 +954,6 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
804
954
|
eventData,
|
|
805
955
|
userData,
|
|
806
956
|
);
|
|
807
|
-
|
|
808
957
|
if (
|
|
809
958
|
existingTenant &&
|
|
810
959
|
existingTenant.role &&
|
|
@@ -825,6 +974,25 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
825
974
|
};
|
|
826
975
|
eventHelper.notification.publish.creation(roleNotification);
|
|
827
976
|
}
|
|
977
|
+
existingTenant?.users
|
|
978
|
+
?.filter((u) => u.status === "pending" && u.id !== userData.id)
|
|
979
|
+
.forEach((previousUser) => {
|
|
980
|
+
const currentUser = newTenant.users?.find((u) => u.id === previousUser.id);
|
|
981
|
+
if (currentUser?.status === "pending") return;
|
|
982
|
+
const invitationNotification: Notification = {
|
|
983
|
+
type: "info",
|
|
984
|
+
subtype: currentUser ? "tenant-invitation-accepted" : "tenant-invitation-declined",
|
|
985
|
+
date: Date.now().toString(),
|
|
986
|
+
status: "unread",
|
|
987
|
+
callToAction: false,
|
|
988
|
+
data: {
|
|
989
|
+
tenant: newTenant.name,
|
|
990
|
+
user: previousUser.id,
|
|
991
|
+
role: previousUser.role,
|
|
992
|
+
},
|
|
993
|
+
};
|
|
994
|
+
eventHelper.notification.publish.creation(invitationNotification);
|
|
995
|
+
});
|
|
828
996
|
if (existingTenant) {
|
|
829
997
|
if (
|
|
830
998
|
!newTenant.resources?.length &&
|
|
@@ -850,6 +1018,12 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
850
1018
|
}
|
|
851
1019
|
|
|
852
1020
|
tenantsMap.set(entityId, newTenant);
|
|
1021
|
+
if (pendingTenantStatus.has(entityId)) {
|
|
1022
|
+
const pendingStatus = pendingTenantStatus.get(entityId)!;
|
|
1023
|
+
tenantsMap.set(entityId, { ...newTenant, status: pendingStatus });
|
|
1024
|
+
pendingTenantStatus.delete(entityId);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
853
1027
|
loadMarketplaceItemsForTenant(entityId, "") //TODO Move to helper
|
|
854
1028
|
.then((items) => {
|
|
855
1029
|
const tenant = tenantsMap.get(entityId);
|
|
@@ -952,13 +1126,17 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
952
1126
|
break;
|
|
953
1127
|
|
|
954
1128
|
case "ca":
|
|
955
|
-
const caResult = handleCAEvent(
|
|
1129
|
+
const caResult = handleCAEvent(
|
|
1130
|
+
eventData,
|
|
1131
|
+
parentParts,
|
|
1132
|
+
tenantsMap,
|
|
1133
|
+
hasRequestingServices,
|
|
1134
|
+
);
|
|
956
1135
|
processResourceResult(
|
|
957
1136
|
caResult,
|
|
958
1137
|
tenantsMap,
|
|
959
1138
|
pendingDomains,
|
|
960
1139
|
eventHelper,
|
|
961
|
-
|
|
962
1140
|
);
|
|
963
1141
|
break;
|
|
964
1142
|
|
|
@@ -1051,6 +1229,7 @@ const handleEvent = async (message: WSMessage) => {
|
|
|
1051
1229
|
eventData,
|
|
1052
1230
|
tokenMap,
|
|
1053
1231
|
userData,
|
|
1232
|
+
deletedTokenIds,
|
|
1054
1233
|
});
|
|
1055
1234
|
|
|
1056
1235
|
if (tokenResult.isUserToken) {
|
|
@@ -1257,6 +1436,7 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1257
1436
|
const channelsInfo = mapChannelsFromApiData(
|
|
1258
1437
|
response.payload.data,
|
|
1259
1438
|
serviceName,
|
|
1439
|
+
service.resources,
|
|
1260
1440
|
);
|
|
1261
1441
|
service.serverChannels = channelsInfo.serverChannels;
|
|
1262
1442
|
service.clientChannels = channelsInfo.clientChannels;
|
|
@@ -1273,7 +1453,7 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1273
1453
|
svcSuccessResult.revisionData
|
|
1274
1454
|
) {
|
|
1275
1455
|
try {
|
|
1276
|
-
|
|
1456
|
+
let updatedService = processRevisionData(
|
|
1277
1457
|
svcSuccessResult.updatedService!,
|
|
1278
1458
|
svcSuccessResult.revisionData,
|
|
1279
1459
|
revisionsMap,
|
|
@@ -1293,6 +1473,28 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1293
1473
|
};
|
|
1294
1474
|
}
|
|
1295
1475
|
|
|
1476
|
+
const resources = updatedService.resources || [];
|
|
1477
|
+
if (resources.length > 0) {
|
|
1478
|
+
const applyIsPublic = (channels: Channel[]): Channel[] =>
|
|
1479
|
+
channels.map((ch) => ({
|
|
1480
|
+
...ch,
|
|
1481
|
+
isPublic:
|
|
1482
|
+
ch.isPublic ||
|
|
1483
|
+
resources.some(
|
|
1484
|
+
(r) =>
|
|
1485
|
+
(r.type === "port" || r.type === "domain") &&
|
|
1486
|
+
(r.name === `${ch.name}_port` || r.name === `${ch.name}_domain`),
|
|
1487
|
+
),
|
|
1488
|
+
}));
|
|
1489
|
+
|
|
1490
|
+
updatedService = {
|
|
1491
|
+
...updatedService,
|
|
1492
|
+
serverChannels: applyIsPublic(updatedService.serverChannels || []),
|
|
1493
|
+
clientChannels: applyIsPublic(updatedService.clientChannels || []),
|
|
1494
|
+
duplexChannels: applyIsPublic(updatedService.duplexChannels || []),
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1296
1498
|
servicesMap.set(svcSuccessResult.serviceId, updatedService);
|
|
1297
1499
|
|
|
1298
1500
|
if (updatedService.role && updatedService.role.length > 0) {
|
|
@@ -1347,16 +1549,18 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1347
1549
|
originalData: originalData as Account,
|
|
1348
1550
|
});
|
|
1349
1551
|
|
|
1552
|
+
const accTenant = (originalData as Account)?.tenant;
|
|
1553
|
+
const accSuccessKey = accTenant ? `${accTenant}/${entityName}` : entityName;
|
|
1350
1554
|
if (accSuccessResult.shouldDelete) {
|
|
1351
|
-
const acc = accountsMap.get(
|
|
1555
|
+
const acc = accountsMap.get(accSuccessKey);
|
|
1352
1556
|
if (acc) {
|
|
1353
|
-
accountsMap.delete(
|
|
1557
|
+
accountsMap.delete(accSuccessKey);
|
|
1354
1558
|
eventHelper.notification.publish.creation(
|
|
1355
1559
|
accSuccessResult.notification,
|
|
1356
1560
|
);
|
|
1357
1561
|
}
|
|
1358
1562
|
} else if (accSuccessResult.updatedAccount) {
|
|
1359
|
-
accountsMap.set(
|
|
1563
|
+
accountsMap.set(accSuccessKey, accSuccessResult.updatedAccount);
|
|
1360
1564
|
if (accSuccessResult.eventType === "created") {
|
|
1361
1565
|
eventHelper.account.publish.created(accSuccessResult.updatedAccount);
|
|
1362
1566
|
} else if (accSuccessResult.eventType === "updated") {
|
|
@@ -1375,10 +1579,12 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1375
1579
|
originalData: originalData as Environment,
|
|
1376
1580
|
});
|
|
1377
1581
|
|
|
1582
|
+
const envOrigData = originalData as Environment;
|
|
1583
|
+
const envSuccessKey = envOrigData?.tenant && envOrigData?.account ? `${envOrigData.tenant}/${envOrigData.account}/${entityName}` : entityName;
|
|
1378
1584
|
if (envSuccessResult.shouldDelete) {
|
|
1379
|
-
environmentsMap.delete(
|
|
1585
|
+
environmentsMap.delete(envSuccessKey);
|
|
1380
1586
|
} else if (envSuccessResult.updatedEnvironment) {
|
|
1381
|
-
environmentsMap.set(
|
|
1587
|
+
environmentsMap.set(envSuccessKey, envSuccessResult.updatedEnvironment);
|
|
1382
1588
|
if (envSuccessResult.notification) {
|
|
1383
1589
|
eventHelper.notification.publish.creation(
|
|
1384
1590
|
envSuccessResult.notification,
|
|
@@ -1406,15 +1612,44 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
|
|
|
1406
1612
|
}
|
|
1407
1613
|
break;
|
|
1408
1614
|
case "token":
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1615
|
+
if (action === "DELETE") {
|
|
1616
|
+
const deletedTokenId = entityName.split("/").pop();
|
|
1617
|
+
if (deletedTokenId) {
|
|
1618
|
+
const tokenDeletionResult = handleTokenDeletion({
|
|
1619
|
+
tokenId: deletedTokenId,
|
|
1620
|
+
tokenMap,
|
|
1621
|
+
userData,
|
|
1622
|
+
deletedTokenIds,
|
|
1623
|
+
});
|
|
1624
|
+
if (tokenDeletionResult.removed) {
|
|
1625
|
+
userData.tokens = tokenDeletionResult.updatedUserTokens;
|
|
1626
|
+
eventHelper.notification.publish.creation(
|
|
1627
|
+
tokenDeletionResult.notification!,
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
} else {
|
|
1632
|
+
const tokenSuccessResult = handleTokenOperationSuccess({
|
|
1633
|
+
responsePayload: response.payload,
|
|
1634
|
+
});
|
|
1635
|
+
tokenMap.set(tokenSuccessResult.token.name, tokenSuccessResult.token);
|
|
1636
|
+
}
|
|
1413
1637
|
break;
|
|
1414
1638
|
case "user":
|
|
1415
1639
|
if (action === "DELETE") {
|
|
1416
|
-
|
|
1417
|
-
|
|
1640
|
+
const newUser = handleUserDelete();
|
|
1641
|
+
userData = newUser;
|
|
1642
|
+
user = new User(newUser);
|
|
1643
|
+
}
|
|
1644
|
+
break;
|
|
1645
|
+
case "resource":
|
|
1646
|
+
if (action === "DELETE") {
|
|
1647
|
+
const tenant = originalData?.payload?.tenant;
|
|
1648
|
+
const resourceName = originalData?.payload?.resource;
|
|
1649
|
+
const resourceType = originalData?.payload?.kind;
|
|
1650
|
+
if (tenant && resourceName && resourceType) {
|
|
1651
|
+
setResourceDeleting(tenantsMap, tenant, resourceName, resourceType);
|
|
1652
|
+
}
|
|
1418
1653
|
}
|
|
1419
1654
|
break;
|
|
1420
1655
|
}
|
|
@@ -1479,10 +1714,12 @@ const handleOperationError = (operation: PendingOperation, error: any) => {
|
|
|
1479
1714
|
error,
|
|
1480
1715
|
});
|
|
1481
1716
|
|
|
1717
|
+
const accErrorTenant = (originalData as Account)?.tenant;
|
|
1718
|
+
const accErrorKey = accErrorTenant ? `${accErrorTenant}/${entityName}` : entityName;
|
|
1482
1719
|
if (accErrorResult.shouldDelete) {
|
|
1483
|
-
accountsMap.delete(
|
|
1720
|
+
accountsMap.delete(accErrorKey);
|
|
1484
1721
|
} else if (accErrorResult.updatedAccount) {
|
|
1485
|
-
accountsMap.set(
|
|
1722
|
+
accountsMap.set(accErrorKey, accErrorResult.updatedAccount);
|
|
1486
1723
|
}
|
|
1487
1724
|
|
|
1488
1725
|
if (accErrorResult.eventType === "creationError") {
|
|
@@ -1492,7 +1729,6 @@ const handleOperationError = (operation: PendingOperation, error: any) => {
|
|
|
1492
1729
|
} else if (accErrorResult.eventType === "deletionError") {
|
|
1493
1730
|
eventHelper.account.publish.deletionError(originalData);
|
|
1494
1731
|
}
|
|
1495
|
-
eventHelper.notification.publish.creation(accErrorResult.notification);
|
|
1496
1732
|
break;
|
|
1497
1733
|
case "environment":
|
|
1498
1734
|
const envErrorResult = handleEnvironmentOperationError({
|
|
@@ -1509,7 +1745,9 @@ const handleOperationError = (operation: PendingOperation, error: any) => {
|
|
|
1509
1745
|
eventHelper.environment.publish.deletionError(originalData);
|
|
1510
1746
|
}
|
|
1511
1747
|
eventHelper.notification.publish.creation(envErrorResult.notification);
|
|
1512
|
-
|
|
1748
|
+
const envErrOrigData = originalData as Environment;
|
|
1749
|
+
const envErrorKey = envErrOrigData?.tenant && envErrOrigData?.account ? `${envErrOrigData.tenant}/${envErrOrigData.account}/${entityName}` : entityName;
|
|
1750
|
+
environmentsMap.set(envErrorKey, envErrorResult.updatedEnvironment);
|
|
1513
1751
|
break;
|
|
1514
1752
|
}
|
|
1515
1753
|
setTimeout(async () => {
|
|
@@ -1540,7 +1778,8 @@ const handleDeleteEvent = async (message: WSMessage) => {
|
|
|
1540
1778
|
}
|
|
1541
1779
|
if (keyParts.environment && !keyParts.service) {
|
|
1542
1780
|
const environmentName = keyParts.environment;
|
|
1543
|
-
const
|
|
1781
|
+
const envDeleteKey = tenantName && keyParts.account ? `${tenantName}/${keyParts.account}/${environmentName}` : environmentName;
|
|
1782
|
+
const deletedEnv = environmentsMap.get(envDeleteKey)!;
|
|
1544
1783
|
eventHelper.environment.publish.deleted(deletedEnv);
|
|
1545
1784
|
const envNotification: Notification = {
|
|
1546
1785
|
type: "success",
|
|
@@ -1555,11 +1794,12 @@ const handleDeleteEvent = async (message: WSMessage) => {
|
|
|
1555
1794
|
},
|
|
1556
1795
|
};
|
|
1557
1796
|
eventHelper.notification.publish.creation(envNotification);
|
|
1558
|
-
environmentsMap.delete(
|
|
1797
|
+
environmentsMap.delete(envDeleteKey);
|
|
1559
1798
|
}
|
|
1560
1799
|
if (keyParts.account && !keyParts.environment) {
|
|
1561
1800
|
const accountName = keyParts.account;
|
|
1562
|
-
const
|
|
1801
|
+
const accountDeleteKey = tenantName ? `${tenantName}/${accountName}` : accountName;
|
|
1802
|
+
const deletedAccount = accountsMap.get(accountDeleteKey)!;
|
|
1563
1803
|
eventHelper.account.publish.deleted(deletedAccount);
|
|
1564
1804
|
const accountNotification: Notification = {
|
|
1565
1805
|
type: "success",
|
|
@@ -1573,7 +1813,7 @@ const handleDeleteEvent = async (message: WSMessage) => {
|
|
|
1573
1813
|
},
|
|
1574
1814
|
};
|
|
1575
1815
|
eventHelper.notification.publish.creation(accountNotification);
|
|
1576
|
-
accountsMap.delete(
|
|
1816
|
+
accountsMap.delete(accountDeleteKey);
|
|
1577
1817
|
}
|
|
1578
1818
|
if (keyParts.domain) {
|
|
1579
1819
|
const domainName = keyParts.domain;
|
|
@@ -1774,24 +2014,17 @@ const handleDeleteEvent = async (message: WSMessage) => {
|
|
|
1774
2014
|
}
|
|
1775
2015
|
}
|
|
1776
2016
|
if (keyParts.token && keyParts.user) {
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
callToAction: false,
|
|
1789
|
-
data: {
|
|
1790
|
-
tokenName: deletedToken.name,
|
|
1791
|
-
tenant: deletedToken.tenant,
|
|
1792
|
-
},
|
|
1793
|
-
};
|
|
1794
|
-
eventHelper.notification.publish.creation(tokenNotification);
|
|
2017
|
+
const tokenDeletionResult = handleTokenDeletion({
|
|
2018
|
+
tokenId: keyParts.token,
|
|
2019
|
+
tokenMap,
|
|
2020
|
+
userData,
|
|
2021
|
+
deletedTokenIds,
|
|
2022
|
+
});
|
|
2023
|
+
if (tokenDeletionResult.removed) {
|
|
2024
|
+
userData.tokens = tokenDeletionResult.updatedUserTokens;
|
|
2025
|
+
eventHelper.notification.publish.creation(
|
|
2026
|
+
tokenDeletionResult.notification!,
|
|
2027
|
+
);
|
|
1795
2028
|
}
|
|
1796
2029
|
}
|
|
1797
2030
|
rebuildHierarchy();
|
|
@@ -1833,9 +2066,10 @@ const getChannelsInfo = async (
|
|
|
1833
2066
|
const updateEnvironmentsCloudProvider = (
|
|
1834
2067
|
accountId: string,
|
|
1835
2068
|
cloudProviderName: string,
|
|
2069
|
+
tenantId?: string,
|
|
1836
2070
|
) => {
|
|
1837
2071
|
environmentsMap.forEach((environment, envId) => {
|
|
1838
|
-
if (environment.account === accountId) {
|
|
2072
|
+
if (environment.account === accountId && (!tenantId || environment.tenant === tenantId)) {
|
|
1839
2073
|
environment.cloudProvider = cloudProviderName;
|
|
1840
2074
|
environmentsMap.set(envId, environment);
|
|
1841
2075
|
}
|
|
@@ -1853,7 +2087,8 @@ const loadReportingForEnvironment = async (environment: Environment) => {
|
|
|
1853
2087
|
reportingData !== null &&
|
|
1854
2088
|
Array.isArray(reportingData)
|
|
1855
2089
|
) {
|
|
1856
|
-
const
|
|
2090
|
+
const envReportKey = environment.tenant && environment.account ? `${environment.tenant}/${environment.account}/${environment.id}` : environment.id;
|
|
2091
|
+
const updatedEnvironment = environmentsMap.get(envReportKey);
|
|
1857
2092
|
|
|
1858
2093
|
if (!updatedEnvironment) {
|
|
1859
2094
|
console.warn(`Environment ${environment.id} not found in map`);
|
|
@@ -1884,7 +2119,7 @@ const loadReportingForEnvironment = async (environment: Environment) => {
|
|
|
1884
2119
|
);
|
|
1885
2120
|
}
|
|
1886
2121
|
|
|
1887
|
-
environmentsMap.set(
|
|
2122
|
+
environmentsMap.set(envReportKey, updatedEnvironment);
|
|
1888
2123
|
}
|
|
1889
2124
|
} catch (error) {
|
|
1890
2125
|
console.error(
|
|
@@ -1916,8 +2151,20 @@ export const updateUserComplete = (updatedUserData: UserData): User => {
|
|
|
1916
2151
|
try {
|
|
1917
2152
|
const preservedNotifications =
|
|
1918
2153
|
userData.notifications || updatedUserData.notifications || [];
|
|
2154
|
+
const incomingTokenNames = new Set(
|
|
2155
|
+
(updatedUserData.tokens || []).map((t) => t.name)
|
|
2156
|
+
);
|
|
2157
|
+
const localOnlyTokens = (userData.tokens || []).filter(
|
|
2158
|
+
(t) => !incomingTokenNames.has(t.name)
|
|
2159
|
+
);
|
|
2160
|
+
const mergedTokens = [...(updatedUserData.tokens || []), ...localOnlyTokens]
|
|
2161
|
+
.filter((t, index, arr) => arr.findIndex(x => x.name === t.name) === index)
|
|
2162
|
+
.filter(t => tokenMap.size === 0 || tokenMap.has(t.name))
|
|
2163
|
+
.filter(t => !deletedTokenIds.has(t.name));
|
|
2164
|
+
|
|
1919
2165
|
userData = {
|
|
1920
2166
|
id: updatedUserData.id || userData.id,
|
|
2167
|
+
created: updatedUserData.created || userData.created,
|
|
1921
2168
|
name: updatedUserData.name || userData.name,
|
|
1922
2169
|
surname: updatedUserData.surname || userData.surname,
|
|
1923
2170
|
provider: updatedUserData.provider || userData.provider || [],
|
|
@@ -1927,7 +2174,7 @@ export const updateUserComplete = (updatedUserData: UserData): User => {
|
|
|
1927
2174
|
"",
|
|
1928
2175
|
organizations: updatedUserData.organizations || [],
|
|
1929
2176
|
clusterTokens: updatedUserData.clusterTokens || [],
|
|
1930
|
-
tokens:
|
|
2177
|
+
tokens: mergedTokens,
|
|
1931
2178
|
tenants: updatedUserData.tenants || [],
|
|
1932
2179
|
axebowPlan:
|
|
1933
2180
|
updatedUserData.axebowPlan || userData.axebowPlan || "freemium",
|
|
@@ -1938,6 +2185,7 @@ export const updateUserComplete = (updatedUserData: UserData): User => {
|
|
|
1938
2185
|
status: updatedUserData.status,
|
|
1939
2186
|
discoveryMethod:
|
|
1940
2187
|
updatedUserData.discoveryMethod || userData.discoveryMethod,
|
|
2188
|
+
promotionInfo: computePromotionInfo(updatedUserData.tenants || userData.tenants || []),
|
|
1941
2189
|
};
|
|
1942
2190
|
user = new User(
|
|
1943
2191
|
userData,
|
|
@@ -1956,7 +2204,7 @@ const resolveServiceStatus = (service: Service): Service => {
|
|
|
1956
2204
|
return service;
|
|
1957
2205
|
}
|
|
1958
2206
|
|
|
1959
|
-
const revisionKey =
|
|
2207
|
+
const revisionKey = `/tenant/${service.tenant}/service/${service.name}/revision/${service.currentRevision}`;
|
|
1960
2208
|
const currentRevision = revisionsMap.get(revisionKey);
|
|
1961
2209
|
|
|
1962
2210
|
if (!currentRevision) {
|
|
@@ -1984,3 +2232,58 @@ const resolveServiceStatus = (service: Service): Service => {
|
|
|
1984
2232
|
status: currentRevision.status,
|
|
1985
2233
|
};
|
|
1986
2234
|
};
|
|
2235
|
+
export const fetchAndStoreMarketplaceSchema = async (
|
|
2236
|
+
item: MarketplaceItem,
|
|
2237
|
+
): Promise<void> => {
|
|
2238
|
+
const tenantsWithItem: string[] = [];
|
|
2239
|
+
tenantsMap.forEach((tenantEntry, tenantId) => {
|
|
2240
|
+
const hasItem = (tenantEntry.marketplaceItems || []).some(
|
|
2241
|
+
(i) => i.name === item.name && i.domain === item.domain && i.artifact === item.artifact,
|
|
2242
|
+
);
|
|
2243
|
+
if (hasItem) tenantsWithItem.push(tenantId);
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
if (tenantsWithItem.length === 0) return;
|
|
2247
|
+
|
|
2248
|
+
await Promise.all(
|
|
2249
|
+
tenantsWithItem.map(async (tenantId) => {
|
|
2250
|
+
try {
|
|
2251
|
+
const response = await makeGlobalWebSocketRequest(
|
|
2252
|
+
"marketplace:artifact_schema",
|
|
2253
|
+
{
|
|
2254
|
+
tenant: tenantId,
|
|
2255
|
+
module: item.name,
|
|
2256
|
+
version: item.version,
|
|
2257
|
+
domain: item.domain,
|
|
2258
|
+
artifact: item.artifact,
|
|
2259
|
+
},
|
|
2260
|
+
30000,
|
|
2261
|
+
"GET",
|
|
2262
|
+
tenantId,
|
|
2263
|
+
);
|
|
2264
|
+
|
|
2265
|
+
const data = response?.data;
|
|
2266
|
+
if (!data) return;
|
|
2267
|
+
|
|
2268
|
+
const nameKeys = Object.keys(data);
|
|
2269
|
+
const schema = processMarketplaceSchemaResponse(data[nameKeys[0]], nameKeys[0]);
|
|
2270
|
+
|
|
2271
|
+
const tenantEntry = tenantsMap.get(tenantId);
|
|
2272
|
+
if (tenantEntry) {
|
|
2273
|
+
tenantEntry.marketplaceItems = tenantEntry.marketplaceItems.map((i) =>
|
|
2274
|
+
i.name === item.name && i.domain === item.domain && i.artifact === item.artifact
|
|
2275
|
+
? { ...i, schema }
|
|
2276
|
+
: i,
|
|
2277
|
+
);
|
|
2278
|
+
tenantsMap.set(tenantId, tenantEntry);
|
|
2279
|
+
}
|
|
2280
|
+
} catch (error) {
|
|
2281
|
+
console.error(`Error loading schema for tenant ${tenantId}:`, error);
|
|
2282
|
+
}
|
|
2283
|
+
}),
|
|
2284
|
+
);
|
|
2285
|
+
|
|
2286
|
+
rebuildHierarchy();
|
|
2287
|
+
await updateUserWithPlatformInfo();
|
|
2288
|
+
};
|
|
2289
|
+
|