@adminide-stack/marketplace-module-server 13.1.2-alpha.9 → 13.1.3-alpha.2
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/lib/graphql/resolvers/installed-extension-resolver.d.ts.map +1 -1
- package/lib/graphql/resolvers/installed-extension-resolver.js +13 -2
- package/lib/graphql/resolvers/installed-extension-resolver.js.map +1 -1
- package/lib/graphql/schemas/installed-extension.graphql +16 -1
- package/lib/graphql/schemas/installed-extension.graphql.js +1 -1
- package/lib/services/installed-extension-service-ext.d.ts +2 -2
- package/lib/services/installed-extension-service-ext.d.ts.map +1 -1
- package/lib/services/installed-extension-service-ext.js +52 -3
- package/lib/services/installed-extension-service-ext.js.map +1 -1
- package/lib/services/installed-extension-service.d.ts +23 -5
- package/lib/services/installed-extension-service.d.ts.map +1 -1
- package/lib/services/installed-extension-service.js +233 -77
- package/lib/services/installed-extension-service.js.map +1 -1
- package/lib/store/repositories/installed-extension-repository.d.ts +7 -1
- package/lib/store/repositories/installed-extension-repository.d.ts.map +1 -1
- package/lib/store/repositories/installed-extension-repository.js +40 -1
- package/lib/store/repositories/installed-extension-repository.js.map +1 -1
- package/lib/templates/services/InstalledExtensionService.ts.template +15 -3
- package/package.json +5 -5
- package/lib/services/installed-extension-service.test.d.ts +0 -1
- package/lib/services/installed-extension-service.test.d.ts.map +0 -1
- package/lib/tests/extension-integration.test.d.ts +0 -1
- package/lib/tests/extension-integration.test.d.ts.map +0 -1
- package/lib/tests/install-extension-graphql.test.d.ts +0 -2
- package/lib/tests/install-extension-graphql.test.d.ts.map +0 -1
- package/lib/tests/test-extension-services.d.ts +0 -1
- package/lib/tests/test-extension-services.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-resolver.d.ts","sourceRoot":"","sources":["../../../src/graphql/resolvers/installed-extension-resolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAuB,UAAU,EAA0C,MAAM,eAAe,CAAC;AAExG,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,EAAE,SAAS,SAAS,CAAC,OAAO,KAAG,
|
|
1
|
+
{"version":3,"file":"installed-extension-resolver.d.ts","sourceRoot":"","sources":["../../../src/graphql/resolvers/installed-extension-resolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAuB,UAAU,EAA0C,MAAM,eAAe,CAAC;AAExG,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,EAAE,SAAS,SAAS,CAAC,OAAO,KAAG,UAkapE,CAAC"}
|
|
@@ -315,7 +315,12 @@ const resolver = (pubsub, logger) => ({
|
|
|
315
315
|
};
|
|
316
316
|
},
|
|
317
317
|
/**
|
|
318
|
-
* Clean up orphaned extensions
|
|
318
|
+
* Clean up orphaned extensions from configuration registry.
|
|
319
|
+
*
|
|
320
|
+
* Identifies and cleans up:
|
|
321
|
+
* 1. Extensions in configuration_registry but NOT in extension registry (deleted extensions)
|
|
322
|
+
* 2. Extensions in configuration_registry but NOT installed for the organization
|
|
323
|
+
* 3. Reports installed extensions without contributions in configuration_registry
|
|
319
324
|
*/
|
|
320
325
|
async cleanupOrphanedExtensions(root, args, {
|
|
321
326
|
userContext,
|
|
@@ -327,11 +332,17 @@ const resolver = (pubsub, logger) => ({
|
|
|
327
332
|
orgId = await slugService.resolveOrganizationSlug(args.orgName);
|
|
328
333
|
}
|
|
329
334
|
const result = await installedExtensionService.cleanupOrphanedExtensions(orgId, userContext.tenantId, args.dryRun || false);
|
|
335
|
+
const totalOrphans = result.notInRegistry.length + result.notInstalled.length;
|
|
330
336
|
return {
|
|
331
337
|
success: true,
|
|
332
338
|
removed: result.removed,
|
|
333
339
|
warnings: result.warnings,
|
|
334
|
-
|
|
340
|
+
notInRegistry: result.notInRegistry,
|
|
341
|
+
notInstalled: result.notInstalled,
|
|
342
|
+
missingContributions: result.missingContributions,
|
|
343
|
+
orphanedInstalledExtensionsRemoved: result.orphanedInstalledExtensionsRemoved,
|
|
344
|
+
orphanedPropertiesRemoved: result.orphanedPropertiesRemoved,
|
|
345
|
+
message: args.dryRun ? `Preview: ${totalOrphans} orphaned extension(s) found - ${result.notInRegistry.length} deleted from registry, ${result.notInstalled.length} not installed, ${result.missingContributions.length} missing contributions, ${result.orphanedPropertiesRemoved.length} orphaned properties in system_extension` : `Cleanup completed: ${result.removed.length} orphaned extension(s) removed, ${result.orphanedPropertiesRemoved.length} orphaned properties removed from system_extension. ${result.missingContributions.length} installed extensions are missing contributions.`
|
|
335
346
|
};
|
|
336
347
|
}
|
|
337
348
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-resolver.js","sources":["../../../src/graphql/resolvers/installed-extension-resolver.ts"],"sourcesContent":[null],"names":[],"mappings":"AAES,MAAA,QAAQ;AACjB,EAAA,kBAAoB,EAAA;AACpB,IAAA,YAA8B,EAAA,CAAU,IAA0C,EAAA,mBAAsB,KAAA;AAExG,MAAA,MAAA
|
|
1
|
+
{"version":3,"file":"installed-extension-resolver.js","sources":["../../../src/graphql/resolvers/installed-extension-resolver.ts"],"sourcesContent":[null],"names":[],"mappings":"AAES,MAAA,QAAQ;AACjB,EAAA,kBAAoB,EAAA;AACpB,IAAA,YAA8B,EAAA,CAAU,IAA0C,EAAA,mBAAsB,KAAA;AAExG,MAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -250,13 +250,28 @@ type ExtensionSyncResponse {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
"""
|
|
253
|
-
Response type for orphaned extension cleanup
|
|
253
|
+
Response type for orphaned extension cleanup.
|
|
254
|
+
Provides detailed information about different types of orphaned extensions found.
|
|
254
255
|
"""
|
|
255
256
|
type OrphanedExtensionCleanupResponse {
|
|
257
|
+
"""Whether the cleanup operation was successful"""
|
|
256
258
|
success: Boolean!
|
|
259
|
+
"""Summary message of the cleanup operation"""
|
|
257
260
|
message: String!
|
|
261
|
+
"""List of extensions that were removed (or would be removed in dry run)"""
|
|
258
262
|
removed: [String!]!
|
|
263
|
+
"""List of warning messages encountered during cleanup"""
|
|
259
264
|
warnings: [String!]!
|
|
265
|
+
"""Extensions found in configuration_registry but NOT in extension registry (deleted extensions)"""
|
|
266
|
+
notInRegistry: [String!]!
|
|
267
|
+
"""Extensions found in configuration_registry but NOT installed for the organization"""
|
|
268
|
+
notInstalled: [String!]!
|
|
269
|
+
"""Installed extensions that have NO contributions in configuration_registry (informational only, not cleaned)"""
|
|
270
|
+
missingContributions: [String!]!
|
|
271
|
+
"""Orphaned installed_extensions entries removed (where referenced extension no longer exists)"""
|
|
272
|
+
orphanedInstalledExtensionsRemoved: [String!]!
|
|
273
|
+
"""Orphaned properties removed from system_extension document (e.g., extensions.CDM/CDM/workflow-extension)"""
|
|
274
|
+
orphanedPropertiesRemoved: [String!]!
|
|
260
275
|
}
|
|
261
276
|
|
|
262
277
|
extend type Query {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var installedExtensionSchema = "\"\"\"\nInstalled extension status enumeration\n\"\"\"\nenum InstalledExtensionStatus {\n installed\n deprecated_installed\n suspended\n orphaned\n force_disabled\n}\n\n\"\"\"\nRegistry status cached from extensionRegistry\n\"\"\"\nenum ExtensionRegistryStatus {\n active\n deprecated\n suspended\n removed\n emergency_removed\n}\n\n\"\"\"\nRuntime activation state\n\"\"\"\nenum ExtensionActivationState {\n enabled\n disabled\n disabled_by_user\n disabled_by_admin\n disabled_by_policy\n}\n\n\"\"\"\nExtension lifecycle management information\n\"\"\"\ntype ExtensionLifecycle @entity(embedded: true) {\n registryStatus: ExtensionRegistryStatus! @column\n isOrphaned: Boolean! @column\n deprecationWarningShown: Boolean! @column\n autoUpdateBlocked: Boolean! @column\n lastRegistryCheck: String! @column\n}\n\n\"\"\"\nAdmin policies for extension behavior\n\"\"\"\ntype ExtensionPolicies @entity(embedded: true) {\n allowOrphanedExecution: Boolean! @column\n requireSecurityUpdates: Boolean! @column\n autoRemoveDeprecated: Boolean! @column\n deprecationGracePeriod: Int! @column\n}\n\n\"\"\"\nExtension settings for user preferences and system state\n\"\"\"\ntype ExtensionSettings @entity(embedded: true) {\n userEnabled: Boolean! @column\n systemEnabled: Boolean! @column\n effectiveEnabled: Boolean! @column\n userConfig: JSON @column\n systemConfig: JSON @column\n}\n\n\"\"\"\nRuntime activation error information\n\"\"\"\ntype ExtensionActivationError @entity(embedded: true) {\n message: String! @column\n code: String! @column\n timestamp: String! @column\n}\n\n\"\"\"\nPerformance metrics\n\"\"\"\ntype ExtensionMetrics @entity(embedded: true) {\n activationTime: Float! @column\n memoryUsage: Float! @column\n lastMeasured: String! @column\n}\n\n\"\"\"\nRuntime information about extension execution\n\"\"\"\ntype ExtensionRuntime @entity(embedded: true) {\n activationState: ExtensionActivationState! @column\n lastActivated: DateTime @column(overrideType: \"Date\")\n lastDeactivated: DateTime @column(overrideType: \"Date\")\n activationError: ExtensionActivationError @embedded\n metrics: ExtensionMetrics @embedded\n}\n\n\"\"\"\nAudit log entry for extension actions\n\"\"\"\ntype ExtensionAuditEntry @entity(embedded: true) {\n action: String! @column\n timestamp: DateTime! @column(overrideType: \"Date\")\n user: UserAccount! @column(overrideType: \"ObjectId\")\n details: JSON @column\n}\n\n\"\"\"\nMain installed extension type\n\"\"\"\ntype InstalledExtension implements Node @entity {\n id: ID! @id\n organization: Organization @column(overrideType: \"ObjectId\")\n extension: RegistryExtension! @column(overrideType: \"ObjectId\")\n installedVersion: String! @column\n status: InstalledExtensionStatus! @column\n lifecycle: ExtensionLifecycle! @embedded\n policies: ExtensionPolicies! @embedded\n settings: ExtensionSettings! @embedded\n runtime: ExtensionRuntime! @embedded\n installedAt: DateTime! @column(overrideType: \"Date\")\n installedBy: UserAccount! @column(overrideType: \"ObjectId\")\n lastUpdated: DateTime @column(overrideType: \"Date\")\n lastUpdatedBy: UserAccount @column(overrideType: \"ObjectId\")\n availableVersion: String @column\n dependencies: [String!] @column\n dependents: [String!] @column\n auditLog: [ExtensionAuditEntry!] @embedded\n \"\"\"The source database collection this installed extension references\"\"\"\n sourceCollection: String @column\n \"\"\"The ObjectId of the specific document in the source collection\"\"\"\n sourceDocumentId: String @column\n \"\"\"Additional metadata for the installed extension\"\"\"\n metadata: JSON @column\n \"\"\"The actual document from the source collection (resolved via DataLoader, returns null if not found or no reference)\"\"\"\n sourceDocument: JSON\n}\n\n\"\"\"\nRegistry information for display\n\"\"\"\ntype ExtensionRegistryInfo {\n displayName: String!\n description: String!\n publisher: String!\n latestVersion: String!\n tags: [String!]!\n categories: [String!]!\n deprecation: JSON\n suspension: JSON\n}\n\n\"\"\"\nNotification action\n\"\"\"\ntype NotificationAction {\n label: String!\n action: String!\n url: String\n extensionSlug: String\n}\n\n\"\"\"\nExtension notification\n\"\"\"\ntype ExtensionNotification {\n type: String!\n severity: String!\n message: String!\n actions: [NotificationAction!]\n}\n\n\"\"\"\nExtension update information\n\"\"\"\ntype ExtensionUpdate {\n extensionSlug: String!\n currentVersion: String!\n availableVersion: String!\n isSecurityUpdate: Boolean!\n}\n\n\"\"\"\nExtension cleanup result\n\"\"\"\ntype ExtensionCleanupResult {\n removed: [String!]!\n deprecated: [String!]!\n warnings: [String!]!\n}\n\n\"\"\"\nInput for installing an extension\n\"\"\"\ninput InstallExtensionInput {\n extensionSlug: String!\n version: String!\n orgName: String\n policies: ExtensionPoliciesInput\n settings: ExtensionSettingsInput\n \"\"\"The source database collection this extension references\"\"\"\n sourceCollection: String\n \"\"\"The ObjectId of the specific document in the source collection\"\"\"\n sourceDocumentId: String\n \"\"\"Additional metadata for the installation\"\"\"\n metadata: JSON\n}\n\n\"\"\"\nInput for extension policies\n\"\"\"\ninput ExtensionPoliciesInput {\n allowOrphanedExecution: Boolean\n requireSecurityUpdates: Boolean\n autoRemoveDeprecated: Boolean\n deprecationGracePeriod: Int\n}\n\n\"\"\"\nInput for extension settings\n\"\"\"\ninput ExtensionSettingsInput {\n userEnabled: Boolean\n systemEnabled: Boolean\n userConfig: JSON\n systemConfig: JSON\n}\n\n\"\"\"\nResponse type for extension mutations\n\"\"\"\ntype ExtensionMutationResponse {\n success: Boolean!\n extension: InstalledExtension\n message: String!\n}\n\n\"\"\"\nResponse type for cleanup operations\n\"\"\"\ntype ExtensionCleanupResponse {\n success: Boolean!\n result: ExtensionCleanupResult\n message: String!\n}\n\n\"\"\"\nResponse type for sync operations\n\"\"\"\ntype ExtensionSyncResponse {\n success: Boolean!\n message: String!\n}\n\n\"\"\"\nResponse type for orphaned extension cleanup
|
|
1
|
+
var installedExtensionSchema = "\"\"\"\nInstalled extension status enumeration\n\"\"\"\nenum InstalledExtensionStatus {\n installed\n deprecated_installed\n suspended\n orphaned\n force_disabled\n}\n\n\"\"\"\nRegistry status cached from extensionRegistry\n\"\"\"\nenum ExtensionRegistryStatus {\n active\n deprecated\n suspended\n removed\n emergency_removed\n}\n\n\"\"\"\nRuntime activation state\n\"\"\"\nenum ExtensionActivationState {\n enabled\n disabled\n disabled_by_user\n disabled_by_admin\n disabled_by_policy\n}\n\n\"\"\"\nExtension lifecycle management information\n\"\"\"\ntype ExtensionLifecycle @entity(embedded: true) {\n registryStatus: ExtensionRegistryStatus! @column\n isOrphaned: Boolean! @column\n deprecationWarningShown: Boolean! @column\n autoUpdateBlocked: Boolean! @column\n lastRegistryCheck: String! @column\n}\n\n\"\"\"\nAdmin policies for extension behavior\n\"\"\"\ntype ExtensionPolicies @entity(embedded: true) {\n allowOrphanedExecution: Boolean! @column\n requireSecurityUpdates: Boolean! @column\n autoRemoveDeprecated: Boolean! @column\n deprecationGracePeriod: Int! @column\n}\n\n\"\"\"\nExtension settings for user preferences and system state\n\"\"\"\ntype ExtensionSettings @entity(embedded: true) {\n userEnabled: Boolean! @column\n systemEnabled: Boolean! @column\n effectiveEnabled: Boolean! @column\n userConfig: JSON @column\n systemConfig: JSON @column\n}\n\n\"\"\"\nRuntime activation error information\n\"\"\"\ntype ExtensionActivationError @entity(embedded: true) {\n message: String! @column\n code: String! @column\n timestamp: String! @column\n}\n\n\"\"\"\nPerformance metrics\n\"\"\"\ntype ExtensionMetrics @entity(embedded: true) {\n activationTime: Float! @column\n memoryUsage: Float! @column\n lastMeasured: String! @column\n}\n\n\"\"\"\nRuntime information about extension execution\n\"\"\"\ntype ExtensionRuntime @entity(embedded: true) {\n activationState: ExtensionActivationState! @column\n lastActivated: DateTime @column(overrideType: \"Date\")\n lastDeactivated: DateTime @column(overrideType: \"Date\")\n activationError: ExtensionActivationError @embedded\n metrics: ExtensionMetrics @embedded\n}\n\n\"\"\"\nAudit log entry for extension actions\n\"\"\"\ntype ExtensionAuditEntry @entity(embedded: true) {\n action: String! @column\n timestamp: DateTime! @column(overrideType: \"Date\")\n user: UserAccount! @column(overrideType: \"ObjectId\")\n details: JSON @column\n}\n\n\"\"\"\nMain installed extension type\n\"\"\"\ntype InstalledExtension implements Node @entity {\n id: ID! @id\n organization: Organization @column(overrideType: \"ObjectId\")\n extension: RegistryExtension! @column(overrideType: \"ObjectId\")\n installedVersion: String! @column\n status: InstalledExtensionStatus! @column\n lifecycle: ExtensionLifecycle! @embedded\n policies: ExtensionPolicies! @embedded\n settings: ExtensionSettings! @embedded\n runtime: ExtensionRuntime! @embedded\n installedAt: DateTime! @column(overrideType: \"Date\")\n installedBy: UserAccount! @column(overrideType: \"ObjectId\")\n lastUpdated: DateTime @column(overrideType: \"Date\")\n lastUpdatedBy: UserAccount @column(overrideType: \"ObjectId\")\n availableVersion: String @column\n dependencies: [String!] @column\n dependents: [String!] @column\n auditLog: [ExtensionAuditEntry!] @embedded\n \"\"\"The source database collection this installed extension references\"\"\"\n sourceCollection: String @column\n \"\"\"The ObjectId of the specific document in the source collection\"\"\"\n sourceDocumentId: String @column\n \"\"\"Additional metadata for the installed extension\"\"\"\n metadata: JSON @column\n \"\"\"The actual document from the source collection (resolved via DataLoader, returns null if not found or no reference)\"\"\"\n sourceDocument: JSON\n}\n\n\"\"\"\nRegistry information for display\n\"\"\"\ntype ExtensionRegistryInfo {\n displayName: String!\n description: String!\n publisher: String!\n latestVersion: String!\n tags: [String!]!\n categories: [String!]!\n deprecation: JSON\n suspension: JSON\n}\n\n\"\"\"\nNotification action\n\"\"\"\ntype NotificationAction {\n label: String!\n action: String!\n url: String\n extensionSlug: String\n}\n\n\"\"\"\nExtension notification\n\"\"\"\ntype ExtensionNotification {\n type: String!\n severity: String!\n message: String!\n actions: [NotificationAction!]\n}\n\n\"\"\"\nExtension update information\n\"\"\"\ntype ExtensionUpdate {\n extensionSlug: String!\n currentVersion: String!\n availableVersion: String!\n isSecurityUpdate: Boolean!\n}\n\n\"\"\"\nExtension cleanup result\n\"\"\"\ntype ExtensionCleanupResult {\n removed: [String!]!\n deprecated: [String!]!\n warnings: [String!]!\n}\n\n\"\"\"\nInput for installing an extension\n\"\"\"\ninput InstallExtensionInput {\n extensionSlug: String!\n version: String!\n orgName: String\n policies: ExtensionPoliciesInput\n settings: ExtensionSettingsInput\n \"\"\"The source database collection this extension references\"\"\"\n sourceCollection: String\n \"\"\"The ObjectId of the specific document in the source collection\"\"\"\n sourceDocumentId: String\n \"\"\"Additional metadata for the installation\"\"\"\n metadata: JSON\n}\n\n\"\"\"\nInput for extension policies\n\"\"\"\ninput ExtensionPoliciesInput {\n allowOrphanedExecution: Boolean\n requireSecurityUpdates: Boolean\n autoRemoveDeprecated: Boolean\n deprecationGracePeriod: Int\n}\n\n\"\"\"\nInput for extension settings\n\"\"\"\ninput ExtensionSettingsInput {\n userEnabled: Boolean\n systemEnabled: Boolean\n userConfig: JSON\n systemConfig: JSON\n}\n\n\"\"\"\nResponse type for extension mutations\n\"\"\"\ntype ExtensionMutationResponse {\n success: Boolean!\n extension: InstalledExtension\n message: String!\n}\n\n\"\"\"\nResponse type for cleanup operations\n\"\"\"\ntype ExtensionCleanupResponse {\n success: Boolean!\n result: ExtensionCleanupResult\n message: String!\n}\n\n\"\"\"\nResponse type for sync operations\n\"\"\"\ntype ExtensionSyncResponse {\n success: Boolean!\n message: String!\n}\n\n\"\"\"\nResponse type for orphaned extension cleanup.\nProvides detailed information about different types of orphaned extensions found.\n\"\"\"\ntype OrphanedExtensionCleanupResponse {\n \"\"\"Whether the cleanup operation was successful\"\"\"\n success: Boolean!\n \"\"\"Summary message of the cleanup operation\"\"\"\n message: String!\n \"\"\"List of extensions that were removed (or would be removed in dry run)\"\"\"\n removed: [String!]!\n \"\"\"List of warning messages encountered during cleanup\"\"\"\n warnings: [String!]!\n \"\"\"Extensions found in configuration_registry but NOT in extension registry (deleted extensions)\"\"\"\n notInRegistry: [String!]!\n \"\"\"Extensions found in configuration_registry but NOT installed for the organization\"\"\"\n notInstalled: [String!]!\n \"\"\"Installed extensions that have NO contributions in configuration_registry (informational only, not cleaned)\"\"\"\n missingContributions: [String!]!\n \"\"\"Orphaned installed_extensions entries removed (where referenced extension no longer exists)\"\"\"\n orphanedInstalledExtensionsRemoved: [String!]!\n \"\"\"Orphaned properties removed from system_extension document (e.g., extensions.CDM/CDM/workflow-extension)\"\"\"\n orphanedPropertiesRemoved: [String!]!\n}\n\nextend type Query {\n \"\"\"\n Get all installed extensions for the current tenant\n \"\"\"\n installedExtensions(enabled: Boolean, orgName: String): [InstalledExtension!]!\n\n \"\"\"\n Get a specific installed extension\n \"\"\"\n installedExtension(extensionSlug: String!): InstalledExtension\n\n \"\"\"\n Get installed extensions that need attention\n \"\"\"\n extensionsNeedingAttention: [InstalledExtension!]!\n\n \"\"\"\n Check for available updates\n \"\"\"\n extensionUpdates(extensionSlug: String): [ExtensionUpdate!]!\n}\n\nextend type Mutation {\n \"\"\"\n Install an extension from the registry\n \"\"\"\n installExtension(input: InstallExtensionInput!): ExtensionMutationResponse!\n\n \"\"\"\n Uninstall an extension\n \"\"\"\n uninstallExtension(extensionSlug: String!, orgName: String): ExtensionMutationResponse!\n\n \"\"\"\n Enable or disable an extension\n \"\"\"\n toggleExtension(extensionSlug: String!, enabled: Boolean!, orgName: String): ExtensionMutationResponse!\n\n \"\"\"\n Update extension settings\n \"\"\"\n updateExtensionSettings(extensionSlug: String!, settings: JSON!, orgName: String): ExtensionMutationResponse!\n\n \"\"\"\n Update an extension to a newer version\n \"\"\"\n updateExtensionVersion(extensionSlug: String!, version: String!, orgName: String): ExtensionMutationResponse!\n\n \"\"\"\n Sync installed extensions with registry\n \"\"\"\n syncExtensionsWithRegistry: ExtensionSyncResponse!\n\n \"\"\"\n Clean up deprecated or removed extensions\n \"\"\"\n cleanupExtensions(dryRun: Boolean): ExtensionCleanupResponse!\n \n \"\"\"\n Clean up orphaned extensions that exist in installed_extensions but not in system_extension registry\n \"\"\"\n cleanupOrphanedExtensions(dryRun: Boolean, orgName: String): OrphanedExtensionCleanupResponse!\n}\n";export{installedExtensionSchema as default};//# sourceMappingURL=installed-extension.graphql.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServiceBroker } from 'moleculer';
|
|
2
2
|
import { CdmLogger } from '@cdm-logger/core';
|
|
3
|
-
import { IRegistryExtensionService, IInstalledExtensionRepository, ISlugService, IRedisCacheManager } from 'common/server';
|
|
3
|
+
import { IRegistryExtensionService, IInstalledExtensionRepository, ISlugService, IRedisCacheManager, IConfigurationRegistryService } from 'common/server';
|
|
4
4
|
import { InstalledExtensionService } from './installed-extension-service';
|
|
5
5
|
/**
|
|
6
6
|
* Extended InstalledExtensionService that handles Moleculer event broadcasting
|
|
@@ -9,7 +9,7 @@ import { InstalledExtensionService } from './installed-extension-service';
|
|
|
9
9
|
export declare class InstalledExtensionServiceExt extends InstalledExtensionService {
|
|
10
10
|
protected slugService: ISlugService;
|
|
11
11
|
protected readonly redisCacheManager: IRedisCacheManager;
|
|
12
|
-
constructor(repository: IInstalledExtensionRepository, registryService: IRegistryExtensionService, slugService: ISlugService, redisCacheManager: IRedisCacheManager, broker: ServiceBroker, logger: CdmLogger.ILogger);
|
|
12
|
+
constructor(repository: IInstalledExtensionRepository, registryService: IRegistryExtensionService, slugService: ISlugService, redisCacheManager: IRedisCacheManager, broker: ServiceBroker, configurationRegistryService: IConfigurationRegistryService, logger: CdmLogger.ILogger);
|
|
13
13
|
private setupEventListeners;
|
|
14
14
|
private dropCache;
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-service-ext.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service-ext.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAgBH,yBAAyB,EACzB,6BAA6B,EAO7B,YAAY,EACZ,kBAAkB,
|
|
1
|
+
{"version":3,"file":"installed-extension-service-ext.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service-ext.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAgBH,yBAAyB,EACzB,6BAA6B,EAO7B,YAAY,EACZ,kBAAkB,EAClB,6BAA6B,EAChC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AA0D1E;;;GAGG;AACH,qBACa,4BAA6B,SAAQ,yBAAyB;IAKnE,SAAS,CAAC,WAAW,EAAE,YAAY;IAEnC,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAE,kBAAkB;gBALJ,UAAU,EAAE,6BAA6B,EAC7C,eAAe,EAAE,yBAAyB,EAEhF,WAAW,EAAE,YAAY,EAEhB,iBAAiB,EAAE,kBAAkB,EACnB,MAAM,EAAE,aAAa,EAE1D,4BAA4B,EAAE,6BAA6B,EAChC,MAAM,EAAE,SAAS,CAAC,OAAO;IAMxD,OAAO,CAAC,mBAAmB;YAmlBb,SAAS;CA8C1B"}
|
|
@@ -52,8 +52,8 @@ function processConfigurationProperties(properties) {
|
|
|
52
52
|
let InstalledExtensionServiceExt = class InstalledExtensionServiceExt extends InstalledExtensionService {
|
|
53
53
|
slugService;
|
|
54
54
|
redisCacheManager;
|
|
55
|
-
constructor(repository, registryService, slugService, redisCacheManager, broker, logger) {
|
|
56
|
-
super(repository, registryService, slugService, broker, logger);
|
|
55
|
+
constructor(repository, registryService, slugService, redisCacheManager, broker, configurationRegistryService, logger) {
|
|
56
|
+
super(repository, registryService, slugService, broker, configurationRegistryService, logger);
|
|
57
57
|
this.slugService = slugService;
|
|
58
58
|
this.redisCacheManager = redisCacheManager;
|
|
59
59
|
this.setupEventListeners();
|
|
@@ -81,6 +81,7 @@ let InstalledExtensionServiceExt = class InstalledExtensionServiceExt extends In
|
|
|
81
81
|
parsedManifest = typeof currentRelease.manifest === 'string' ? JSON.parse(currentRelease.manifest) : currentRelease.manifest;
|
|
82
82
|
console.log('Parsed manifest keys:', Object.keys(parsedManifest));
|
|
83
83
|
contributes = parsedManifest.contributes;
|
|
84
|
+
console.log('Contributes section:', contributes);
|
|
84
85
|
if (contributes) {
|
|
85
86
|
console.log('Contributes keys:', Object.keys(contributes));
|
|
86
87
|
console.log('Configuration exists:', !!contributes.configuration);
|
|
@@ -155,6 +156,41 @@ let InstalledExtensionServiceExt = class InstalledExtensionServiceExt extends In
|
|
|
155
156
|
} else {
|
|
156
157
|
console.log('No UILayout contributions found in manifest');
|
|
157
158
|
}
|
|
159
|
+
// Extract Secrets contributions if they exist (same structure as configuration)
|
|
160
|
+
if (contributes && contributes.secrets) {
|
|
161
|
+
console.log('Secrets contributions found, processing...');
|
|
162
|
+
const secretsContributions = Array.isArray(contributes.secrets) ? contributes.secrets : [contributes.secrets];
|
|
163
|
+
console.log(`Found ${secretsContributions.length} Secrets contribution(s) in manifest`);
|
|
164
|
+
// Format Secrets contributions (same structure as configuration)
|
|
165
|
+
const secretsNodes = secretsContributions.map(secret => {
|
|
166
|
+
// Process properties to convert scope strings to numbers (same as configuration)
|
|
167
|
+
const processedProperties = processConfigurationProperties(secret.properties || {});
|
|
168
|
+
return {
|
|
169
|
+
id: ContributionFragmentName.Settings,
|
|
170
|
+
title: secret.title || registryExtension.name,
|
|
171
|
+
description: secret.description,
|
|
172
|
+
properties: processedProperties,
|
|
173
|
+
type: secret.type || ['object']
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
if (secretsNodes.length > 0) {
|
|
177
|
+
console.log(`Registering ${secretsNodes.length} Secrets configuration nodes`);
|
|
178
|
+
try {
|
|
179
|
+
// Register Secrets contributions (exact same as configuration, just different schema)
|
|
180
|
+
await this.broker.call('ConfigurationRegistryService.registerSecretsContributions', {
|
|
181
|
+
tenantId: event.tenantId,
|
|
182
|
+
extensionID: event.extensionSlug,
|
|
183
|
+
secretsNodes
|
|
184
|
+
});
|
|
185
|
+
console.log(`✅ Secrets contributions registered successfully for ${event.extensionSlug}`);
|
|
186
|
+
} catch (secretsError) {
|
|
187
|
+
console.error('❌ Failed to register Secrets contributions:', secretsError);
|
|
188
|
+
// This is not fatal, log and continue
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
console.log('No Secrets contributions found in manifest');
|
|
193
|
+
}
|
|
158
194
|
} else {
|
|
159
195
|
console.log('No current release or manifest found for version:', registryExtension.version);
|
|
160
196
|
}
|
|
@@ -328,6 +364,19 @@ let InstalledExtensionServiceExt = class InstalledExtensionServiceExt extends In
|
|
|
328
364
|
console.warn('⚠️ STEP 1.5 WARNING: UILayout contributions removal failed (may not exist):', uiLayoutError);
|
|
329
365
|
// This is not fatal - extension might not have had UILayout contributions
|
|
330
366
|
}
|
|
367
|
+
// STEP 1.6: Remove Secrets contributions (if any exist)
|
|
368
|
+
console.log('\n🗑️ STEP 1.6: Removing Secrets contributions (if exist)');
|
|
369
|
+
try {
|
|
370
|
+
console.log('Attempting to remove Secrets contributions...');
|
|
371
|
+
await this.broker.call('ConfigurationRegistryService.removeSecretsContributions', {
|
|
372
|
+
tenantId: event.tenantId,
|
|
373
|
+
extensionID: event.extensionSlug
|
|
374
|
+
});
|
|
375
|
+
console.log('✅ STEP 1.6 SUCCESS: Secrets contributions removed');
|
|
376
|
+
} catch (secretsError) {
|
|
377
|
+
console.warn('⚠️ STEP 1.6 WARNING: Secrets contributions removal failed (may not exist):', secretsError);
|
|
378
|
+
// This is not fatal - extension might not have had Secrets contributions
|
|
379
|
+
}
|
|
331
380
|
// STEP 2: MANDATORY - Remove extension node from system_extension document
|
|
332
381
|
console.log('\n🔧 STEP 2: Unregistering extension from SYSTEM_EXTENSION (MANDATORY)');
|
|
333
382
|
try {
|
|
@@ -515,4 +564,4 @@ let InstalledExtensionServiceExt = class InstalledExtensionServiceExt extends In
|
|
|
515
564
|
}
|
|
516
565
|
}
|
|
517
566
|
};
|
|
518
|
-
InstalledExtensionServiceExt = __decorate([injectable(), __param(0, inject(SERVER_TYPES.IInstalledExtensionRepository)), __param(1, inject(SERVER_TYPES.IRegistryExtensionService)), __param(2, inject(SERVER_TYPES.SlugService)), __param(3, inject(SERVER_TYPES.IRedisCacheManager)), __param(4, inject(CommonType.MOLECULER_BROKER)), __param(5, inject(CommonType.LOGGER)), __metadata("design:paramtypes", [Object, Object, Object, Object, ServiceBroker, Object])], InstalledExtensionServiceExt);export{InstalledExtensionServiceExt};//# sourceMappingURL=installed-extension-service-ext.js.map
|
|
567
|
+
InstalledExtensionServiceExt = __decorate([injectable(), __param(0, inject(SERVER_TYPES.IInstalledExtensionRepository)), __param(1, inject(SERVER_TYPES.IRegistryExtensionService)), __param(2, inject(SERVER_TYPES.SlugService)), __param(3, inject(SERVER_TYPES.IRedisCacheManager)), __param(4, inject(CommonType.MOLECULER_BROKER)), __param(5, inject(SERVER_TYPES.ContributionNodeRegistryService)), __param(6, inject(CommonType.LOGGER)), __metadata("design:paramtypes", [Object, Object, Object, Object, ServiceBroker, Object, Object])], InstalledExtensionServiceExt);export{InstalledExtensionServiceExt};//# sourceMappingURL=installed-extension-service-ext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-service-ext.js","sources":["../../src/services/installed-extension-service-ext.ts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installed-extension-service-ext.js","sources":["../../src/services/installed-extension-service-ext.ts"],"sourcesContent":[null],"names":[],"mappings":"qbA6FG;AACH;;AAQQ;mCALgE,CAAA,KAAA,EAAA;AAepE,EAAA,IAAA,OAAQ,KAAA,KAAA,QAAmB,EAAA;gBAmlBb,CAAA;AA8CjB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -2,12 +2,13 @@ import { CdmLogger } from '@cdm-logger/core';
|
|
|
2
2
|
import { ServiceBroker } from 'moleculer';
|
|
3
3
|
import { Disposable, DisposableCollection, Emitter } from '@adminide-stack/core';
|
|
4
4
|
import { BaseService2 } from '@common-stack/store-mongo';
|
|
5
|
-
import { IInstalledExtensionModel, ICreateInstalledExtensionInput, IUpdateInstalledExtensionInput, IRegistryExtensionService, IInstalledExtensionFilter, IInstalledExtensionService, IInstalledExtensionRepository, IExtensionInstalledEvent, IExtensionUninstalledEvent, IExtensionEnabledEvent, IExtensionDisabledEvent, IExtensionUpdatedEvent, IExtensionVersionUpdatedEvent, IExtensionStatusChangedEvent, IExtensionActivationFailedEvent, IExtensionDeprecatedEvent, IExtensionOrphanedEvent, IExtensionConfigurationUpdatedEvent, IExtensionSyncCompletedEvent, IExtensionCleanupCompletedEvent, AsDomainType, ISlugService } from 'common/server';
|
|
5
|
+
import { IInstalledExtensionModel, ICreateInstalledExtensionInput, IUpdateInstalledExtensionInput, IRegistryExtensionService, IInstalledExtensionFilter, IInstalledExtensionService, IInstalledExtensionRepository, IExtensionInstalledEvent, IExtensionUninstalledEvent, IExtensionEnabledEvent, IExtensionDisabledEvent, IExtensionUpdatedEvent, IExtensionVersionUpdatedEvent, IExtensionStatusChangedEvent, IExtensionActivationFailedEvent, IExtensionDeprecatedEvent, IExtensionOrphanedEvent, IExtensionConfigurationUpdatedEvent, IExtensionSyncCompletedEvent, IExtensionCleanupCompletedEvent, AsDomainType, ISlugService, IConfigurationRegistryService } from 'common/server';
|
|
6
6
|
export declare class InstalledExtensionService extends BaseService2<IInstalledExtensionModel> implements IInstalledExtensionService, Disposable {
|
|
7
7
|
private installedExtensionRepository;
|
|
8
8
|
protected registryExtensionService: IRegistryExtensionService;
|
|
9
9
|
protected slugService: ISlugService;
|
|
10
10
|
protected broker: ServiceBroker;
|
|
11
|
+
protected configurationRegistryService: IConfigurationRegistryService;
|
|
11
12
|
protected readonly onExtensionInstalled: Emitter<IExtensionInstalledEvent>;
|
|
12
13
|
protected readonly onExtensionUninstalled: Emitter<IExtensionUninstalledEvent>;
|
|
13
14
|
protected readonly onExtensionEnabled: Emitter<IExtensionEnabledEvent>;
|
|
@@ -23,7 +24,7 @@ export declare class InstalledExtensionService extends BaseService2<IInstalledEx
|
|
|
23
24
|
protected readonly onExtensionOrphaned: Emitter<IExtensionOrphanedEvent>;
|
|
24
25
|
protected readonly toDispose: DisposableCollection;
|
|
25
26
|
private logger;
|
|
26
|
-
constructor(installedExtensionRepository: IInstalledExtensionRepository, registryExtensionService: IRegistryExtensionService, slugService: ISlugService, broker: ServiceBroker, logger: CdmLogger.ILogger);
|
|
27
|
+
constructor(installedExtensionRepository: IInstalledExtensionRepository, registryExtensionService: IRegistryExtensionService, slugService: ISlugService, broker: ServiceBroker, configurationRegistryService: IConfigurationRegistryService, logger: CdmLogger.ILogger);
|
|
27
28
|
dispose(): void;
|
|
28
29
|
private resolveExtensionSlug;
|
|
29
30
|
/**
|
|
@@ -94,14 +95,31 @@ export declare class InstalledExtensionService extends BaseService2<IInstalledEx
|
|
|
94
95
|
warnings: string[];
|
|
95
96
|
}>;
|
|
96
97
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
98
|
+
* Comprehensive cleanup of orphaned extensions.
|
|
99
|
+
*
|
|
100
|
+
* This method identifies and cleans up extensions that are:
|
|
101
|
+
* 1. In configuration_registry but NOT in extension registry (deleted extensions)
|
|
102
|
+
* 2. In configuration_registry but NOT installed for the organization
|
|
103
|
+
* 3. Reports installed extensions without contributions in configuration_registry
|
|
104
|
+
*
|
|
105
|
+
* @param orgId - The organization ID
|
|
106
|
+
* @param tenantId - The tenant ID
|
|
107
|
+
* @param dryRun - If true, only reports what would be cleaned without actually cleaning
|
|
108
|
+
* @returns Cleanup result with removed extensions, warnings, and missing contributions
|
|
100
109
|
*/
|
|
101
110
|
cleanupOrphanedExtensions(orgId: string, tenantId: string, dryRun?: boolean): Promise<{
|
|
102
111
|
removed: string[];
|
|
103
112
|
warnings: string[];
|
|
113
|
+
notInRegistry: string[];
|
|
114
|
+
notInstalled: string[];
|
|
115
|
+
missingContributions: string[];
|
|
116
|
+
orphanedInstalledExtensionsRemoved: string[];
|
|
117
|
+
orphanedPropertiesRemoved: string[];
|
|
104
118
|
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Check if an extension slug is a system extension that should not be cleaned up
|
|
121
|
+
*/
|
|
122
|
+
private isSystemExtension;
|
|
105
123
|
/**
|
|
106
124
|
* Check if update is a security update (simplified logic)
|
|
107
125
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-service.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACH,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,EAE9B,yBAAyB,EAEzB,yBAAyB,EAEzB,0BAA0B,EAC1B,6BAA6B,EAC7B,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,6BAA6B,EAC7B,4BAA4B,EAC5B,+BAA+B,EAC/B,yBAAyB,EACzB,uBAAuB,EACvB,mCAAmC,EACnC,4BAA4B,EAC5B,+BAA+B,EAC/B,YAAY,EACZ,YAAY,
|
|
1
|
+
{"version":3,"file":"installed-extension-service.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {__decorate,__param,__metadata}from'tslib';import {injectable,inject}from'inversify';import'@cdm-logger/core';import {ServiceBroker}from'moleculer';import {CommonType}from'@common-stack/core';import {Emitter,DisposableCollection}from'@adminide-stack/core';import {BaseService2}from'@common-stack/store-mongo';import {SERVER_TYPES,DB_COLL_NAMES,ExtensionActivationState}from'common/server';var InstalledExtensionService_1;
|
|
1
|
+
import {__decorate,__param,__metadata}from'tslib';import {injectable,inject}from'inversify';import'@cdm-logger/core';import {ServiceBroker}from'moleculer';import {CommonType}from'@common-stack/core';import {Emitter,DisposableCollection}from'@adminide-stack/core';import {BaseService2}from'@common-stack/store-mongo';import {SERVER_TYPES,DB_COLL_NAMES,ExtensionActivationState,ContributionSchemaId}from'common/server';var InstalledExtensionService_1;
|
|
2
2
|
let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExtensionService extends BaseService2 {
|
|
3
3
|
installedExtensionRepository;
|
|
4
4
|
registryExtensionService;
|
|
5
5
|
slugService;
|
|
6
6
|
broker;
|
|
7
|
+
configurationRegistryService;
|
|
7
8
|
onExtensionInstalled = new Emitter();
|
|
8
9
|
onExtensionUninstalled = new Emitter();
|
|
9
10
|
onExtensionEnabled = new Emitter();
|
|
@@ -19,12 +20,13 @@ let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExt
|
|
|
19
20
|
onExtensionOrphaned = new Emitter();
|
|
20
21
|
toDispose = new DisposableCollection(this.onExtensionInstalled, this.onExtensionUninstalled, this.onExtensionEnabled, this.onExtensionDisabled, this.onExtensionUpdated, this.onExtensionVersionUpdated, this.onExtensionConfigurationUpdated, this.onExtensionSyncCompleted, this.onExtensionCleanupCompleted, this.onExtensionStatusChanged, this.onExtensionActivationFailed, this.onExtensionDeprecated, this.onExtensionOrphaned);
|
|
21
22
|
logger;
|
|
22
|
-
constructor(installedExtensionRepository, registryExtensionService, slugService, broker, logger) {
|
|
23
|
+
constructor(installedExtensionRepository, registryExtensionService, slugService, broker, configurationRegistryService, logger) {
|
|
23
24
|
super(installedExtensionRepository);
|
|
24
25
|
this.installedExtensionRepository = installedExtensionRepository;
|
|
25
26
|
this.registryExtensionService = registryExtensionService;
|
|
26
27
|
this.slugService = slugService;
|
|
27
28
|
this.broker = broker;
|
|
29
|
+
this.configurationRegistryService = configurationRegistryService;
|
|
28
30
|
this.logger = logger.child({
|
|
29
31
|
className: InstalledExtensionService_1.name
|
|
30
32
|
});
|
|
@@ -527,99 +529,253 @@ let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExt
|
|
|
527
529
|
return result;
|
|
528
530
|
}
|
|
529
531
|
/**
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
532
|
+
* Comprehensive cleanup of orphaned extensions.
|
|
533
|
+
*
|
|
534
|
+
* This method identifies and cleans up extensions that are:
|
|
535
|
+
* 1. In configuration_registry but NOT in extension registry (deleted extensions)
|
|
536
|
+
* 2. In configuration_registry but NOT installed for the organization
|
|
537
|
+
* 3. Reports installed extensions without contributions in configuration_registry
|
|
538
|
+
*
|
|
539
|
+
* @param orgId - The organization ID
|
|
540
|
+
* @param tenantId - The tenant ID
|
|
541
|
+
* @param dryRun - If true, only reports what would be cleaned without actually cleaning
|
|
542
|
+
* @returns Cleanup result with removed extensions, warnings, and missing contributions
|
|
533
543
|
*/
|
|
534
544
|
async cleanupOrphanedExtensions(orgId, tenantId, dryRun) {
|
|
535
545
|
const result = {
|
|
536
546
|
removed: [],
|
|
537
|
-
warnings: []
|
|
547
|
+
warnings: [],
|
|
548
|
+
notInRegistry: [],
|
|
549
|
+
notInstalled: [],
|
|
550
|
+
missingContributions: [],
|
|
551
|
+
orphanedInstalledExtensionsRemoved: [],
|
|
552
|
+
orphanedPropertiesRemoved: []
|
|
538
553
|
};
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
schemaId: 'configuration',
|
|
550
|
-
tenantId,
|
|
551
|
-
extensionName: 'system_extension'
|
|
554
|
+
this.logger.info('🔍 [ORPHAN CLEANUP] Starting comprehensive orphaned extension cleanup...');
|
|
555
|
+
this.logger.info(' OrgId: %s, TenantId: %s, DryRun: %s', orgId, tenantId, dryRun);
|
|
556
|
+
try {
|
|
557
|
+
// Step 1: Get all extensions from the extension registry
|
|
558
|
+
const registryExtensions = await this.registryExtensionService.findExtensions(1000, '', true, true, []);
|
|
559
|
+
const registryExtensionSlugs = new Set();
|
|
560
|
+
for (const ext of registryExtensions || []) {
|
|
561
|
+
if (ext.extensionSlug) {
|
|
562
|
+
registryExtensionSlugs.add(ext.extensionSlug);
|
|
563
|
+
}
|
|
552
564
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
565
|
+
this.logger.info(' 📦 Found %d extensions in extension registry', registryExtensionSlugs.size);
|
|
566
|
+
// Step 2: Get all installed extensions for this organization
|
|
567
|
+
const installedExtensions = await this.installedExtensionRepository.findExtensions({
|
|
568
|
+
orgId
|
|
569
|
+
});
|
|
570
|
+
const installedExtensionSlugs = new Set();
|
|
571
|
+
const installedExtensionMap = new Map();
|
|
572
|
+
// Track orphaned installed_extensions entries (where extension doesn't exist in extensions collection)
|
|
573
|
+
const orphanedInstalledExtensions = [];
|
|
574
|
+
for (const ext of installedExtensions) {
|
|
575
|
+
// Get the slug from registry for this extension
|
|
576
|
+
let extensionSlug = null;
|
|
577
|
+
// The repository returns objects with 'id' (via toJSON virtuals), not '_id'
|
|
578
|
+
const installedExtId = ext.id || ext._id;
|
|
579
|
+
try {
|
|
580
|
+
const registryExt = await this.registryExtensionService.get(String(ext.extension));
|
|
581
|
+
if (registryExt) {
|
|
582
|
+
extensionSlug = registryExt.extensionSlug;
|
|
583
|
+
} else {
|
|
584
|
+
// Extension doesn't exist in extensions collection - this is an orphan
|
|
585
|
+
orphanedInstalledExtensions.push({
|
|
586
|
+
installedExtensionId: String(installedExtId),
|
|
587
|
+
extensionId: String(ext.extension),
|
|
588
|
+
reason: 'Extension not found in extensions collection'
|
|
589
|
+
});
|
|
590
|
+
this.logger.warn(' ⚠️ Orphaned installed_extension: %s references non-existent extension: %s', installedExtId, ext.extension);
|
|
566
591
|
}
|
|
567
|
-
})
|
|
592
|
+
} catch (error) {
|
|
593
|
+
// If we get an error looking up the extension, it's likely orphaned
|
|
594
|
+
orphanedInstalledExtensions.push({
|
|
595
|
+
installedExtensionId: String(installedExtId),
|
|
596
|
+
extensionId: String(ext.extension),
|
|
597
|
+
reason: `Registry lookup failed: ${error.message}`
|
|
598
|
+
});
|
|
599
|
+
this.logger.warn(' ⚠️ Orphaned installed_extension: %s - lookup failed for extension: %s', installedExtId, ext.extension);
|
|
600
|
+
}
|
|
601
|
+
if (extensionSlug) {
|
|
602
|
+
installedExtensionSlugs.add(extensionSlug);
|
|
603
|
+
installedExtensionMap.set(extensionSlug, ext);
|
|
604
|
+
}
|
|
568
605
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
606
|
+
this.logger.info(' 📥 Found %d installed extensions', installedExtensionSlugs.size);
|
|
607
|
+
this.logger.info(' 🗑️ Found %d orphaned installed_extension entries', orphanedInstalledExtensions.length);
|
|
608
|
+
// Step 3: Get extensions from configuration registry (system_extension document)
|
|
609
|
+
// Scan both configuration and uiLayout schemas
|
|
610
|
+
const configExtensionSlugs = new Set();
|
|
611
|
+
const schemas = [ContributionSchemaId.Configuration, ContributionSchemaId.UiLayout];
|
|
612
|
+
for (const schemaId of schemas) {
|
|
613
|
+
try {
|
|
614
|
+
const systemExtensionDoc = await this.configurationRegistryService.getConfigurationNodeRegistry({
|
|
615
|
+
schemaId,
|
|
616
|
+
tenantId,
|
|
617
|
+
extensionName: 'system_extension'
|
|
618
|
+
});
|
|
619
|
+
if (systemExtensionDoc && typeof systemExtensionDoc === 'object' && 'configurationNodes' in systemExtensionDoc) {
|
|
620
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
621
|
+
const configNodes = systemExtensionDoc.configurationNodes || [];
|
|
622
|
+
// Extract from settings node properties
|
|
623
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
624
|
+
const settingsNode = configNodes.find(node => node.id === 'settings');
|
|
625
|
+
if (settingsNode?.properties) {
|
|
626
|
+
Object.keys(settingsNode.properties).forEach(key => {
|
|
627
|
+
if (key.startsWith('extensions.')) {
|
|
628
|
+
configExtensionSlugs.add(key.replace('extensions.', ''));
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
// Also extract from extensionInfo of each node
|
|
633
|
+
for (const node of configNodes) {
|
|
634
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
635
|
+
if (node.extensionInfo?.id) {
|
|
636
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
637
|
+
configExtensionSlugs.add(node.extensionInfo.id);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
this.logger.info(' 📋 Found extensions in %s schema (total: %d)', schemaId, configExtensionSlugs.size);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
this.logger.error(' ❌ Failed to get %s configuration registry:', schemaId, error);
|
|
644
|
+
result.warnings.push(`Failed to get ${schemaId} configuration registry: ${error.message}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
this.logger.info(' 📋 Total unique extensions in configuration registry: %d', configExtensionSlugs.size);
|
|
648
|
+
// Step 4: Identify orphans
|
|
649
|
+
// 4a. Extensions in config registry but NOT in extension registry (deleted)
|
|
650
|
+
for (const configSlug of configExtensionSlugs) {
|
|
651
|
+
// Skip system extensions
|
|
652
|
+
if (this.isSystemExtension(configSlug)) {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (!registryExtensionSlugs.has(configSlug)) {
|
|
656
|
+
this.logger.warn(' ⚠️ Extension in config but NOT in registry: %s', configSlug);
|
|
657
|
+
result.notInRegistry.push(configSlug);
|
|
581
658
|
}
|
|
582
|
-
} catch (error) {
|
|
583
|
-
this.logger.warn(`Failed to fetch registry extension ${extensionId}:`, error);
|
|
584
659
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
660
|
+
// 4b. Extensions in config registry but NOT installed for this org
|
|
661
|
+
for (const configSlug of configExtensionSlugs) {
|
|
662
|
+
if (this.isSystemExtension(configSlug)) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
// Only check if it's in the registry (not already flagged as deleted)
|
|
666
|
+
if (registryExtensionSlugs.has(configSlug) && !installedExtensionSlugs.has(configSlug)) {
|
|
667
|
+
this.logger.warn(' ⚠️ Extension in config but NOT installed: %s', configSlug);
|
|
668
|
+
result.notInstalled.push(configSlug);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// 4c. Installed extensions without contributions in config registry
|
|
672
|
+
for (const installedSlug of installedExtensionSlugs) {
|
|
673
|
+
if (!configExtensionSlugs.has(installedSlug) && !this.isSystemExtension(installedSlug)) {
|
|
674
|
+
this.logger.warn(' ⚠️ Installed extension missing contributions: %s', installedSlug);
|
|
675
|
+
result.missingContributions.push(installedSlug);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// Step 5: Clean up orphaned extensions from configuration registry
|
|
679
|
+
const orphansToClean = [...result.notInRegistry, ...result.notInstalled];
|
|
680
|
+
for (const orphanSlug of orphansToClean) {
|
|
592
681
|
if (!dryRun) {
|
|
593
682
|
try {
|
|
594
|
-
// Remove from
|
|
595
|
-
await this.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
683
|
+
// Remove from configuration registry (all schemas) - removes extension-specific documents
|
|
684
|
+
await this.configurationRegistryService.removeConfigurationNodeRegistry({
|
|
685
|
+
schemaId: ContributionSchemaId.Configuration,
|
|
686
|
+
tenantId,
|
|
687
|
+
extensionName: orphanSlug
|
|
688
|
+
});
|
|
689
|
+
this.logger.info(' ✅ Removed %s from configuration schema', orphanSlug);
|
|
690
|
+
await this.configurationRegistryService.removeConfigurationNodeRegistry({
|
|
691
|
+
schemaId: ContributionSchemaId.UiLayout,
|
|
692
|
+
tenantId,
|
|
693
|
+
extensionName: orphanSlug
|
|
604
694
|
});
|
|
605
|
-
|
|
606
|
-
|
|
695
|
+
this.logger.info(' ✅ Removed %s from uiLayout schema', orphanSlug);
|
|
696
|
+
// If extension exists in installed_extensions but not in registry, remove it
|
|
697
|
+
if (result.notInRegistry.includes(orphanSlug) && installedExtensionMap.has(orphanSlug)) {
|
|
698
|
+
const installedExt = installedExtensionMap.get(orphanSlug);
|
|
699
|
+
if (installedExt) {
|
|
700
|
+
await this.installedExtensionRepository.deleteExtension(orgId, String(installedExt.extension));
|
|
701
|
+
this.logger.info(' ✅ Removed %s from installed_extensions', orphanSlug);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
result.removed.push(orphanSlug);
|
|
607
705
|
} catch (cleanupError) {
|
|
608
|
-
|
|
609
|
-
result.warnings.push(`Failed to cleanup ${
|
|
706
|
+
this.logger.error(' ❌ Failed to cleanup %s:', orphanSlug, cleanupError);
|
|
707
|
+
result.warnings.push(`Failed to cleanup ${orphanSlug}: ${cleanupError.message}`);
|
|
610
708
|
}
|
|
611
709
|
} else {
|
|
612
|
-
|
|
613
|
-
result.removed.push(
|
|
710
|
+
this.logger.info(' [DRY RUN] Would remove: %s', orphanSlug);
|
|
711
|
+
result.removed.push(orphanSlug);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// Step 5b: Clean up orphaned properties from system_extension document
|
|
715
|
+
// Using the dedicated method in ConfigurationRegistryService
|
|
716
|
+
this.logger.info('🧹 Cleaning up orphaned properties from system_extension document...');
|
|
717
|
+
if (orphansToClean.length > 0) {
|
|
718
|
+
try {
|
|
719
|
+
const propertyCleanupResult = await this.configurationRegistryService.cleanupOrphanedExtensionProperties(tenantId, orphansToClean, dryRun);
|
|
720
|
+
if (propertyCleanupResult.success) {
|
|
721
|
+
result.orphanedPropertiesRemoved.push(...propertyCleanupResult.removedProperties);
|
|
722
|
+
for (const prop of propertyCleanupResult.removedProperties) {
|
|
723
|
+
if (dryRun) {
|
|
724
|
+
this.logger.info(' [DRY RUN] Would remove property: %s from system_extension', prop);
|
|
725
|
+
} else {
|
|
726
|
+
this.logger.info(' ✅ Removed property: %s from system_extension', prop);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (propertyCleanupResult.warnings.length > 0) {
|
|
731
|
+
result.warnings.push(...propertyCleanupResult.warnings);
|
|
732
|
+
}
|
|
733
|
+
} catch (systemDocError) {
|
|
734
|
+
this.logger.error(' ❌ Failed to clean up system_extension properties:', systemDocError);
|
|
735
|
+
result.warnings.push(`Failed to clean up system_extension properties: ${systemDocError.message}`);
|
|
614
736
|
}
|
|
615
|
-
} else {
|
|
616
|
-
console.log(`✅ Extension ${extensionSlug} is registered in system_extension`);
|
|
617
737
|
}
|
|
738
|
+
// Step 6: Clean up orphaned installed_extensions entries
|
|
739
|
+
// (entries where the referenced extension doesn't exist in extensions collection)
|
|
740
|
+
this.logger.info('🗑️ Cleaning up %d orphaned installed_extension entries...', orphanedInstalledExtensions.length);
|
|
741
|
+
for (const orphan of orphanedInstalledExtensions) {
|
|
742
|
+
if (!dryRun) {
|
|
743
|
+
try {
|
|
744
|
+
// Cast to any to access deleteById method which may not be in interface
|
|
745
|
+
await this.installedExtensionRepository.deleteById(orphan.installedExtensionId);
|
|
746
|
+
this.logger.info(' ✅ Removed orphaned installed_extension: %s (extension: %s)', orphan.installedExtensionId, orphan.extensionId);
|
|
747
|
+
result.orphanedInstalledExtensionsRemoved.push(orphan.extensionId);
|
|
748
|
+
} catch (deleteError) {
|
|
749
|
+
this.logger.error(' ❌ Failed to delete orphaned installed_extension %s:', orphan.installedExtensionId, deleteError);
|
|
750
|
+
result.warnings.push(`Failed to delete orphaned installed_extension ${orphan.installedExtensionId}: ${deleteError.message}`);
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
this.logger.info(' [DRY RUN] Would remove orphaned installed_extension: %s (extension: %s)', orphan.installedExtensionId, orphan.extensionId);
|
|
754
|
+
result.orphanedInstalledExtensionsRemoved.push(orphan.extensionId);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
this.logger.info('✅ [ORPHAN CLEANUP] Complete');
|
|
758
|
+
this.logger.info(' 📊 Summary:');
|
|
759
|
+
this.logger.info(' - Not in registry (deleted): %d', result.notInRegistry.length);
|
|
760
|
+
this.logger.info(' - Not installed: %d', result.notInstalled.length);
|
|
761
|
+
this.logger.info(' - Missing contributions: %d', result.missingContributions.length);
|
|
762
|
+
this.logger.info(' - Removed from config: %d', result.removed.length);
|
|
763
|
+
this.logger.info(' - Orphaned properties removed from system_extension: %d', result.orphanedPropertiesRemoved.length);
|
|
764
|
+
this.logger.info(' - Orphaned installed_extensions removed: %d', result.orphanedInstalledExtensionsRemoved.length);
|
|
765
|
+
this.logger.info(' - Warnings: %d', result.warnings.length);
|
|
766
|
+
return result;
|
|
767
|
+
} catch (error) {
|
|
768
|
+
this.logger.error('❌ [ORPHAN CLEANUP] Failed:', error);
|
|
769
|
+
result.warnings.push(`Cleanup failed: ${error.message}`);
|
|
770
|
+
return result;
|
|
618
771
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Check if an extension slug is a system extension that should not be cleaned up
|
|
775
|
+
*/
|
|
776
|
+
isSystemExtension(extensionSlug) {
|
|
777
|
+
const systemExtensions = ['vscode', 'system', 'core', 'default', 'settings', 'preferences', 'extensions', 'system_extension'];
|
|
778
|
+
return systemExtensions.some(sys => extensionSlug === sys || extensionSlug.startsWith(`${sys}.`) || extensionSlug.startsWith(`${sys}/`));
|
|
623
779
|
}
|
|
624
780
|
/**
|
|
625
781
|
* Check if update is a security update (simplified logic)
|
|
@@ -630,4 +786,4 @@ let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExt
|
|
|
630
786
|
return false;
|
|
631
787
|
}
|
|
632
788
|
};
|
|
633
|
-
InstalledExtensionService = InstalledExtensionService_1 = __decorate([injectable(), __param(0, inject(SERVER_TYPES.IInstalledExtensionRepository)), __param(1, inject(SERVER_TYPES.IRegistryExtensionService)), __param(2, inject(SERVER_TYPES.SlugService)), __param(3, inject(CommonType.MOLECULER_BROKER)), __param(4, inject(CommonType.LOGGER)), __metadata("design:paramtypes", [Object, Object, Object, ServiceBroker, Object])], InstalledExtensionService);export{InstalledExtensionService};//# sourceMappingURL=installed-extension-service.js.map
|
|
789
|
+
InstalledExtensionService = InstalledExtensionService_1 = __decorate([injectable(), __param(0, inject(SERVER_TYPES.IInstalledExtensionRepository)), __param(1, inject(SERVER_TYPES.IRegistryExtensionService)), __param(2, inject(SERVER_TYPES.SlugService)), __param(3, inject(CommonType.MOLECULER_BROKER)), __param(4, inject(SERVER_TYPES.ContributionNodeRegistryService)), __param(5, inject(CommonType.LOGGER)), __metadata("design:paramtypes", [Object, Object, Object, ServiceBroker, Object, Object])], InstalledExtensionService);export{InstalledExtensionService};//# sourceMappingURL=installed-extension-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-service.js","sources":["../../src/services/installed-extension-service.ts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installed-extension-service.js","sources":["../../src/services/installed-extension-service.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -29,7 +29,13 @@ export declare class InstalledExtensionRepository extends BaseMongoRepository<II
|
|
|
29
29
|
*/
|
|
30
30
|
findExtensions(query: IInstalledExtensionFilter): Promise<AsDomainType<IInstalledExtensionModel>[]>;
|
|
31
31
|
/**
|
|
32
|
-
* Delete an installed extension
|
|
32
|
+
* Delete an installed extension by its document _id
|
|
33
|
+
* Used for orphan cleanup when extension reference is invalid
|
|
34
|
+
* @param id - Can be a MongoDB ObjectId string, UUID string, or any valid document _id
|
|
35
|
+
*/
|
|
36
|
+
deleteById(id: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Delete an installed extension by organization and extension IDs
|
|
33
39
|
*/
|
|
34
40
|
deleteExtension(orgId: string, extensionId: string): Promise<boolean>;
|
|
35
41
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-repository.d.ts","sourceRoot":"","sources":["../../../src/store/repositories/installed-extension-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EACH,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,EAC9B,yBAAyB,EACzB,6BAA6B,EAC7B,YAAY,EAEf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAGrC,qBACa,4BACT,SAAQ,mBAAmB,CAAC,wBAAwB,CACpD,YAAW,6BAA6B;gBAGP,EAAE,EAAE,QAAQ,CAAC,UAAU,EAClC,MAAM,EAAE,SAAS,CAAC,OAAO,EACP,OAAO,CAAC,EAAE,GAAG;IAS/C,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC;IAe5G,uBAAuB,CACzB,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC;IAanD,eAAe,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC;IA+BzE;;OAEG;IACU,eAAe,CACxB,KAAK,EAAE,8BAA8B,GACtC,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC;IAuDlD;;OAEG;IACU,eAAe,CACxB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,8BAA8B,GACvC,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC;IAuBzD;;OAEG;IACU,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC;IAYhH;;OAEG;IACU,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,EAAE,CAAC;IAatG;;;OAGG;IACU,cAAc,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,EAAE,CAAC;IAgDhH;;OAEG;IACU,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYlF;;OAEG;IACU,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAkCnF;;OAEG;IACU,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgB1E;;OAEG;IACU,oBAAoB,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CAAC,yBAAyB,CAAC,EAC1C,MAAM,EAAE,OAAO,CAAC,8BAA8B,CAAC,GAChD,OAAO,CAAC,MAAM,CAAC;IAWlB;;OAEG;IACU,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACvD,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,CAAC;QACrD,YAAY,EAAE,KAAK,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,SAAS,EAAE,MAAM,EAAE,CAAC;YACpB,UAAU,EAAE,MAAM,EAAE,CAAC;SACxB,CAAC,CAAC;KACN,CAAC;IAoBF;;OAEG;IACU,YAAY,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5E;;OAEG;IACU,mBAAmB,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC,EAAE,CAAC;IAiBpD;;OAEG;IACU,kBAAkB,CAC3B,OAAO,EAAE,KAAK,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,OAAO,CAAC;KACvB,CAAC,GACH,OAAO,CAAC,IAAI,CAAC;CAyBnB"}
|
|
1
|
+
{"version":3,"file":"installed-extension-repository.d.ts","sourceRoot":"","sources":["../../../src/store/repositories/installed-extension-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -252,7 +252,46 @@ import {__decorate,__param,__metadata}from'tslib';import {injectable,inject,opti
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
/**
|
|
255
|
-
* Delete an installed extension
|
|
255
|
+
* Delete an installed extension by its document _id
|
|
256
|
+
* Used for orphan cleanup when extension reference is invalid
|
|
257
|
+
* @param id - Can be a MongoDB ObjectId string, UUID string, or any valid document _id
|
|
258
|
+
*/
|
|
259
|
+
async deleteById(id) {
|
|
260
|
+
try {
|
|
261
|
+
this.logger.info('deleteById called', {
|
|
262
|
+
id,
|
|
263
|
+
idType: typeof id
|
|
264
|
+
});
|
|
265
|
+
// Check if it's a valid MongoDB ObjectId (24 hex characters)
|
|
266
|
+
const isValidObjectId = /^[a-fA-F0-9]{24}$/.test(id);
|
|
267
|
+
const query = isValidObjectId ? {
|
|
268
|
+
_id: new mongoose.Types.ObjectId(id)
|
|
269
|
+
} : {
|
|
270
|
+
_id: id
|
|
271
|
+
}; // Use as-is for UUID or other string IDs
|
|
272
|
+
this.logger.info('deleteById query', {
|
|
273
|
+
query: JSON.stringify(query),
|
|
274
|
+
isValidObjectId
|
|
275
|
+
});
|
|
276
|
+
// Use the base class delete method which is known to work
|
|
277
|
+
const result = await this.delete(query);
|
|
278
|
+
this.logger.info('Deleted installed extension by id', {
|
|
279
|
+
id,
|
|
280
|
+
isValidObjectId,
|
|
281
|
+
deleted: result
|
|
282
|
+
});
|
|
283
|
+
return result;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
this.logger.error('Error deleting installed extension by id', {
|
|
286
|
+
error: error instanceof Error ? error.message : String(error),
|
|
287
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
288
|
+
id
|
|
289
|
+
});
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Delete an installed extension by organization and extension IDs
|
|
256
295
|
*/
|
|
257
296
|
async deleteExtension(orgId, extensionId) {
|
|
258
297
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-repository.js","sources":["../../../src/store/repositories/installed-extension-repository.ts"],"sourcesContent":[null],"names":[],"mappings":"mWAiCU,IAAA,4BAA4B,qCAAkC,SAAY;AAe1E,EAAA,WAAA,CAAA,EAAA,EAAA,MAAA,EAAA,OACF,EAAA;IAeE,KAAA,CAAA,2BAAwB,EAAA,EAAA,EAAA,MAAA,EAAA,OAA4B,CAAA;AA+B1D,IAAA,IAAA,CAAA,EAAA,EAAA;;AAEG,IAAA;IACU,IAAA,CAAA,MAAA,GAAA,MACT,CAAK,KAAE,CAAA;AAwDX,MAAA,SAAA,EAAA;;AAEG,EAAA;QACU,oBACF,EAAA,aACP,EAAA;AAyBJ,IAAA,IAAA;;AAEG;AACU,MAAA,IAAA,CAAO,MAAM,CAAA,IAAE,CAAA;AAY5B,QAAA,KAAA;;AAEG,OAAA,CAAA;AACU,MAAA,OAAA,IAAA;AAab,IAAA,CAAA,CAAA,OAAA,KAAA,EAAA;;;AAGG,QAAA,KAAA;AACU,QAAA;AAgDb,OAAA,CAAA
|
|
1
|
+
{"version":3,"file":"installed-extension-repository.js","sources":["../../../src/store/repositories/installed-extension-repository.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -186,14 +186,26 @@ export interface IInstalledExtensionService extends IBaseService<IInstalledExten
|
|
|
186
186
|
}>;
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
189
|
+
* Comprehensive cleanup of orphaned extensions from configuration registry.
|
|
190
|
+
*
|
|
191
|
+
* Identifies and cleans up:
|
|
192
|
+
* 1. Extensions in configuration_registry but NOT in extension registry (deleted extensions)
|
|
193
|
+
* 2. Extensions in configuration_registry but NOT installed for the organization
|
|
194
|
+
* 3. Reports installed extensions without contributions in configuration_registry
|
|
195
|
+
* 4. Orphaned installed_extension records pointing to non-existent extensions
|
|
196
|
+
* 5. Orphaned extension properties in system_extension document
|
|
190
197
|
*/
|
|
191
198
|
cleanupOrphanedExtensions(
|
|
192
199
|
orgId: string,
|
|
193
200
|
tenantId: string,
|
|
194
201
|
dryRun?: boolean,
|
|
195
202
|
): Promise<{
|
|
196
|
-
removed: string[]; // Array of extension slugs
|
|
197
|
-
warnings: string[];
|
|
203
|
+
removed: string[]; // Array of extension slugs that were removed
|
|
204
|
+
warnings: string[]; // Warning messages encountered during cleanup
|
|
205
|
+
notInRegistry: string[]; // Extensions in config but not in extension registry (deleted)
|
|
206
|
+
notInstalled: string[]; // Extensions in config but not installed for org
|
|
207
|
+
missingContributions: string[]; // Installed extensions without config contributions
|
|
208
|
+
orphanedInstalledExtensionsRemoved: string[]; // Orphaned installed_extension records removed
|
|
209
|
+
orphanedPropertiesRemoved: string[]; // Orphaned properties removed from system_extension
|
|
198
210
|
}>;
|
|
199
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminide-stack/marketplace-module-server",
|
|
3
|
-
"version": "13.1.
|
|
3
|
+
"version": "13.1.3-alpha.2",
|
|
4
4
|
"description": "Sample core for higher packages to depend on",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"watch": "yarn build:lib:watch"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@adminide-stack/extension-api": "13.1.
|
|
24
|
+
"@adminide-stack/extension-api": "13.1.3-alpha.2",
|
|
25
25
|
"nanoid": "^5.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"common": "13.1.
|
|
28
|
+
"common": "13.1.3-alpha.2"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"graphql-tag": ">=2.0.0"
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
]
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "1049eb9b6d34f64db1d0e1a85a21924eda9d4ff9",
|
|
59
58
|
"typescript": {
|
|
60
59
|
"definition": "lib/index.d.ts"
|
|
61
|
-
}
|
|
60
|
+
},
|
|
61
|
+
"gitHead": "394adad29e2a114eb360b13076eab61df7d2d14a"
|
|
62
62
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=installed-extension-service.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"installed-extension-service.test.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=extension-integration.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extension-integration.test.d.ts","sourceRoot":"","sources":["../../src/tests/extension-integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"install-extension-graphql.test.d.ts","sourceRoot":"","sources":["../../src/tests/install-extension-graphql.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=test-extension-services.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-extension-services.d.ts","sourceRoot":"","sources":["../../src/tests/test-extension-services.ts"],"names":[],"mappings":""}
|