@adminide-stack/marketplace-module-server 12.0.4-alpha.41 → 12.0.4-alpha.412
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/Readme.md +321 -0
- package/lib/containers/module.d.ts +8 -0
- package/lib/containers/module.d.ts.map +1 -1
- package/lib/containers/module.js +15 -5
- package/lib/containers/module.js.map +1 -1
- package/lib/dataloaders/index.d.ts +1 -0
- package/lib/dataloaders/index.d.ts.map +1 -0
- package/lib/demo/test-graphql-examples.d.ts +73 -0
- package/lib/demo/test-graphql-examples.d.ts.map +1 -0
- package/lib/graphql/resolvers/form-templates-resolver.d.ts +220 -0
- package/lib/graphql/resolvers/form-templates-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/form-templates-resolver.js +170 -0
- package/lib/graphql/resolvers/form-templates-resolver.js.map +1 -0
- package/lib/graphql/resolvers/gallery-resolver.d.ts +15 -0
- package/lib/graphql/resolvers/gallery-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/gallery-resolver.js +35 -0
- package/lib/graphql/resolvers/gallery-resolver.js.map +1 -0
- package/lib/graphql/resolvers/index.d.ts +247 -1
- package/lib/graphql/resolvers/index.d.ts.map +1 -1
- package/lib/graphql/resolvers/index.js +1 -0
- package/lib/graphql/resolvers/index.js.map +1 -0
- package/lib/graphql/resolvers/installed-extension-resolver.d.ts +5 -0
- package/lib/graphql/resolvers/installed-extension-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/installed-extension-resolver.js +289 -0
- package/lib/graphql/resolvers/installed-extension-resolver.js.map +1 -0
- package/lib/graphql/resolvers/marketplace-form-resolver.d.ts +13 -0
- package/lib/graphql/resolvers/marketplace-form-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/marketplace-form-resolver.js +90 -0
- package/lib/graphql/resolvers/marketplace-form-resolver.js.map +1 -0
- package/lib/graphql/resolvers/publisher-analytics-resolver.d.ts +14 -0
- package/lib/graphql/resolvers/publisher-analytics-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/publisher-analytics-resolver.js +221 -0
- package/lib/graphql/resolvers/publisher-analytics-resolver.js.map +1 -0
- package/lib/graphql/resolvers/publisher-resolver.d.ts +5 -0
- package/lib/graphql/resolvers/publisher-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/publisher-resolver.js +176 -0
- package/lib/graphql/resolvers/publisher-resolver.js.map +1 -0
- package/lib/graphql/resolvers/registry-extension-resolver.d.ts +5 -0
- package/lib/graphql/resolvers/registry-extension-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/registry-extension-resolver.js +46 -0
- package/lib/graphql/resolvers/registry-extension-resolver.js.map +1 -0
- package/lib/graphql/schemas/extension-pricing.graphql +546 -0
- package/lib/graphql/schemas/extension-pricing.graphql.js +1 -0
- package/lib/graphql/schemas/extension-pricing.graphql.js.map +1 -0
- package/lib/graphql/schemas/extension-registry.graphql +107 -0
- package/lib/graphql/schemas/extension-registry.graphql.js +1 -0
- package/lib/graphql/schemas/extension-registry.graphql.js.map +1 -0
- package/lib/graphql/schemas/form-templates.graphql +269 -0
- package/lib/graphql/schemas/form-templates.graphql.js +1 -0
- package/lib/graphql/schemas/form-templates.graphql.js.map +1 -0
- package/lib/graphql/schemas/gallery-schema.graphql +247 -0
- package/lib/graphql/schemas/gallery-schema.graphql.js +1 -0
- package/lib/graphql/schemas/gallery-schema.graphql.js.map +1 -0
- package/lib/graphql/schemas/index.d.ts.map +1 -1
- package/lib/graphql/schemas/index.js +3 -4
- package/lib/graphql/schemas/index.js.map +1 -1
- package/lib/graphql/schemas/installed-extension.graphql +309 -0
- package/lib/graphql/schemas/installed-extension.graphql.js +1 -0
- package/lib/graphql/schemas/installed-extension.graphql.js.map +1 -0
- package/lib/graphql/schemas/publisher-analytics.graphql +305 -0
- package/lib/graphql/schemas/publisher-analytics.graphql.js +1 -0
- package/lib/graphql/schemas/publisher-analytics.graphql.js.map +1 -0
- package/lib/graphql/schemas/publisher.graphql +376 -0
- package/lib/graphql/schemas/publisher.graphql.js +1 -0
- package/lib/graphql/schemas/publisher.graphql.js.map +1 -0
- package/lib/graphql/schemas/service.graphql +196 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/module.d.ts +1 -1
- package/lib/module.d.ts.map +1 -1
- package/lib/module.js +10 -23
- package/lib/module.js.map +1 -1
- package/lib/plugins/extension-moleculer-service.d.ts +86 -0
- package/lib/plugins/extension-moleculer-service.d.ts.map +1 -0
- package/lib/plugins/index.d.ts +2 -0
- package/lib/plugins/index.d.ts.map +1 -0
- package/lib/services/extension-gallery-repository.d.ts +17 -0
- package/lib/services/extension-gallery-repository.d.ts.map +1 -0
- package/lib/services/extension-gallery-repository.js +192 -0
- package/lib/services/extension-gallery-repository.js.map +1 -0
- package/lib/services/extension-gallery-service-new.d.ts +39 -0
- package/lib/services/extension-gallery-service-new.d.ts.map +1 -0
- package/lib/services/extension-gallery-service.d.ts +30 -0
- package/lib/services/extension-gallery-service.d.ts.map +1 -0
- package/lib/services/extension-gallery-service.js +311 -0
- package/lib/services/extension-gallery-service.js.map +1 -0
- package/lib/services/index.d.ts +6 -1
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/installed-extension-service-ext.d.ts +16 -0
- package/lib/services/installed-extension-service-ext.d.ts.map +1 -0
- package/lib/services/installed-extension-service-ext.js +485 -0
- package/lib/services/installed-extension-service-ext.js.map +1 -0
- package/lib/services/installed-extension-service.d.ts +96 -0
- package/lib/services/installed-extension-service.d.ts.map +1 -0
- package/lib/services/installed-extension-service.js +521 -0
- package/lib/services/installed-extension-service.js.map +1 -0
- package/lib/services/installed-extension-service.test.d.ts +1 -0
- package/lib/services/installed-extension-service.test.d.ts.map +1 -0
- package/lib/services/publisher-analytics-service.d.ts +128 -0
- package/lib/services/publisher-analytics-service.d.ts.map +1 -0
- package/lib/services/publisher-event-service.d.ts +48 -0
- package/lib/services/publisher-event-service.d.ts.map +1 -0
- package/lib/services/publisher-event-service.js +296 -0
- package/lib/services/publisher-event-service.js.map +1 -0
- package/lib/services/publisher-service-context.d.ts +1 -0
- package/lib/services/publisher-service-context.d.ts.map +1 -0
- package/lib/services/publisher-service.d.ts +60 -0
- package/lib/services/publisher-service.d.ts.map +1 -0
- package/lib/services/publisher-service.js +134 -0
- package/lib/services/publisher-service.js.map +1 -0
- package/lib/store/index.d.ts +1 -1
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/models/index.d.ts +2 -1
- package/lib/store/models/index.d.ts.map +1 -1
- package/lib/store/models/installed-extension-model.d.ts +4 -0
- package/lib/store/models/installed-extension-model.d.ts.map +1 -0
- package/lib/store/models/installed-extension-model.js +269 -0
- package/lib/store/models/installed-extension-model.js.map +1 -0
- package/lib/store/models/publisher-event-model.d.ts +11 -0
- package/lib/store/models/publisher-event-model.d.ts.map +1 -0
- package/lib/store/models/publisher-model.d.ts +5 -0
- package/lib/store/models/publisher-model.d.ts.map +1 -0
- package/lib/store/models/publisher-model.js +103 -0
- package/lib/store/models/publisher-model.js.map +1 -0
- package/lib/store/models/publisher-stats-model.d.ts +1 -0
- package/lib/store/models/publisher-stats-model.d.ts.map +1 -0
- package/lib/store/repositories/index.d.ts +3 -0
- package/lib/store/repositories/index.d.ts.map +1 -0
- package/lib/store/repositories/installed-extension-repository.d.ts +73 -0
- package/lib/store/repositories/installed-extension-repository.d.ts.map +1 -0
- package/lib/store/repositories/installed-extension-repository.js +442 -0
- package/lib/store/repositories/installed-extension-repository.js.map +1 -0
- package/lib/store/repositories/publisher-analytics-repository.d.ts +1 -0
- package/lib/store/repositories/publisher-analytics-repository.d.ts.map +1 -0
- package/lib/store/repositories/publisher-repository.d.ts +19 -0
- package/lib/store/repositories/publisher-repository.d.ts.map +1 -0
- package/lib/store/repositories/publisher-repository.js +87 -0
- package/lib/store/repositories/publisher-repository.js.map +1 -0
- package/lib/templates/constants/DB_COLL_NAMES.ts.template +5 -0
- package/lib/templates/constants/SERVER_TYPES.ts.template +10 -4
- package/lib/templates/repositories/ExtensionGalleryRepository.ts.template +44 -0
- package/lib/templates/repositories/InstalledExtensionRepository.ts.template +94 -0
- package/lib/templates/repositories/MarketplacePublisherRepository.ts.template +24 -0
- package/lib/templates/repositories/RegistryExtensionRepository.ts.template +10 -15
- package/lib/templates/services/ExtensionGalleryDataLoader.ts.template +2 -0
- package/lib/templates/services/ExtensionGalleryService.ts.template +79 -0
- package/lib/templates/services/InstalledExtensionDataLoader.ts.template +2 -0
- package/lib/templates/services/InstalledExtensionService.ts.template +181 -0
- package/lib/templates/services/MarketplacePublisherService.ts.template +49 -0
- package/lib/templates/services/PublisherEventService.ts.template +56 -0
- package/lib/templates/services/RegistryExtensionService.ts.template +62 -18
- package/lib/tests/extension-integration.test.d.ts +1 -0
- package/lib/tests/extension-integration.test.d.ts.map +1 -0
- package/lib/tests/install-extension-graphql.test.d.ts +2 -0
- package/lib/tests/install-extension-graphql.test.d.ts.map +1 -0
- package/lib/tests/test-extension-services.d.ts +1 -0
- package/lib/tests/test-extension-services.d.ts.map +1 -0
- package/lib/utils/publisherValidation.d.ts +23 -0
- package/lib/utils/publisherValidation.d.ts.map +1 -0
- package/lib/utils/publisherValidation.js +144 -0
- package/lib/utils/publisherValidation.js.map +1 -0
- package/package.json +15 -7
- package/lib/graphql/resolvers/resolvers.d.ts +0 -2
- package/lib/graphql/resolvers/resolvers.d.ts.map +0 -1
- package/lib/graphql/resolvers/resolvers.js +0 -167
- package/lib/graphql/resolvers/resolvers.js.map +0 -1
- package/lib/graphql/schemas/extension.graphql +0 -57
- package/lib/graphql/schemas/extension.graphql.js +0 -1
- package/lib/graphql/schemas/extension.graphql.js.map +0 -1
- package/lib/services/extension-service.d.ts +0 -54
- package/lib/services/extension-service.d.ts.map +0 -1
- package/lib/services/extension-service.js +0 -42
- package/lib/services/extension-service.js.map +0 -1
- package/lib/store/models/registry-extension-model.d.ts +0 -10
- package/lib/store/models/registry-extension-model.d.ts.map +0 -1
- package/lib/store/models/registry-extension-model.js +0 -62
- package/lib/store/models/registry-extension-model.js.map +0 -1
- package/lib/store/repository/index.d.ts +0 -2
- package/lib/store/repository/index.d.ts.map +0 -1
- package/lib/store/repository/registry-extension-repository.d.ts +0 -31
- package/lib/store/repository/registry-extension-repository.d.ts.map +0 -1
- package/lib/store/repository/registry-extension-repository.js +0 -135
- package/lib/store/repository/registry-extension-repository.js.map +0 -1
|
@@ -0,0 +1,521 @@
|
|
|
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;
|
|
2
|
+
let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExtensionService extends BaseService2 {
|
|
3
|
+
installedExtensionRepository;
|
|
4
|
+
registryExtensionService;
|
|
5
|
+
slugService;
|
|
6
|
+
broker;
|
|
7
|
+
onExtensionInstalled = new Emitter();
|
|
8
|
+
onExtensionUninstalled = new Emitter();
|
|
9
|
+
onExtensionEnabled = new Emitter();
|
|
10
|
+
onExtensionDisabled = new Emitter();
|
|
11
|
+
onExtensionUpdated = new Emitter();
|
|
12
|
+
onExtensionVersionUpdated = new Emitter();
|
|
13
|
+
onExtensionConfigurationUpdated = new Emitter();
|
|
14
|
+
onExtensionSyncCompleted = new Emitter();
|
|
15
|
+
onExtensionCleanupCompleted = new Emitter();
|
|
16
|
+
onExtensionStatusChanged = new Emitter();
|
|
17
|
+
onExtensionActivationFailed = new Emitter();
|
|
18
|
+
onExtensionDeprecated = new Emitter();
|
|
19
|
+
onExtensionOrphaned = new Emitter();
|
|
20
|
+
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
|
+
logger;
|
|
22
|
+
constructor(installedExtensionRepository, registryExtensionService, slugService, broker, logger) {
|
|
23
|
+
super(installedExtensionRepository);
|
|
24
|
+
this.installedExtensionRepository = installedExtensionRepository;
|
|
25
|
+
this.registryExtensionService = registryExtensionService;
|
|
26
|
+
this.slugService = slugService;
|
|
27
|
+
this.broker = broker;
|
|
28
|
+
this.logger = logger.child({
|
|
29
|
+
className: InstalledExtensionService_1.name
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
dispose() {
|
|
33
|
+
this.toDispose.dispose();
|
|
34
|
+
}
|
|
35
|
+
resolveExtensionSlug(extensionSlug) {
|
|
36
|
+
return this.slugService.resolveSlugWithProvider(DB_COLL_NAMES.ExtensionRegistries, 'extensionSlug', extensionSlug);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Install a new extension from the registry
|
|
40
|
+
*/
|
|
41
|
+
async installExtension(input, tenantId) {
|
|
42
|
+
this.logger.debug('Installing extension [%s] for organization [%s] with input [%j]', input.extensionSlug, input.orgId, input);
|
|
43
|
+
if (!input.orgId) {
|
|
44
|
+
this.logger.error('Organization ID missing for extension installation [%s]', input.extensionSlug);
|
|
45
|
+
throw new Error('Organization ID (orgId) is required for extension installation');
|
|
46
|
+
}
|
|
47
|
+
this.logger.info('Installing extension [%s] for organization [%s]', input.extensionSlug, input.orgId);
|
|
48
|
+
// Check if extension already exists for this organization
|
|
49
|
+
this.logger.debug('Resolving extension slug [%s] to extension ID', input.extensionSlug);
|
|
50
|
+
const extensionId = await this.resolveExtensionSlug(input.extensionSlug);
|
|
51
|
+
this.logger.debug('Extension slug [%s] resolved to ID [%s]', input.extensionSlug, extensionId);
|
|
52
|
+
this.logger.debug('Checking if extension [%s] already exists for organization [%s]', extensionId, input.orgId);
|
|
53
|
+
const existing = await this.installedExtensionRepository.exists(input.orgId, extensionId);
|
|
54
|
+
if (existing) {
|
|
55
|
+
this.logger.warn('Extension [%s] already installed for organization [%s]', input.extensionSlug, input.orgId);
|
|
56
|
+
throw new Error(`Extension ${input.extensionSlug} is already installed for organization ${input.orgId}`);
|
|
57
|
+
}
|
|
58
|
+
// Get extension from registry to validate
|
|
59
|
+
this.logger.debug('Fetching extension [%s] from registry', input.extensionSlug);
|
|
60
|
+
const registryExtension = await this.registryExtensionService.findExtension(input.extensionSlug);
|
|
61
|
+
if (!registryExtension) {
|
|
62
|
+
this.logger.error('Extension [%s] not found in registry', input.extensionSlug);
|
|
63
|
+
throw new Error(`Extension ${input.extensionSlug} not found in registry`);
|
|
64
|
+
}
|
|
65
|
+
this.logger.debug('Registry extension found [%j]', {
|
|
66
|
+
id: registryExtension.id,
|
|
67
|
+
version: registryExtension.version,
|
|
68
|
+
name: registryExtension.name
|
|
69
|
+
});
|
|
70
|
+
// Delegate to the internal install method (validation already done)
|
|
71
|
+
this.logger.debug('Delegating to doInstallExtension for [%s]', input.extensionSlug);
|
|
72
|
+
return this.doInstallExtension(input, registryExtension, tenantId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Internal method to perform the actual installation after validation
|
|
76
|
+
*/
|
|
77
|
+
async doInstallExtension(input, registryExtension, tenantId) {
|
|
78
|
+
this.logger.debug('doInstallExtension input [%j]', input);
|
|
79
|
+
this.logger.debug('doInstallExtension registryExtension [%j]', {
|
|
80
|
+
id: registryExtension.id,
|
|
81
|
+
version: registryExtension.version,
|
|
82
|
+
name: registryExtension.name
|
|
83
|
+
});
|
|
84
|
+
const versionToUse = input.version || registryExtension.version;
|
|
85
|
+
this.logger.debug('Version resolution: input.version=[%s], registryExtension.version=[%s], using=[%s]', input.version, registryExtension.version, versionToUse);
|
|
86
|
+
// Create the installed extension record with proper defaults
|
|
87
|
+
const installedExtension = await this.installedExtensionRepository.create({
|
|
88
|
+
organization: input.orgId,
|
|
89
|
+
extensionSlug: input.extensionSlug,
|
|
90
|
+
extension: registryExtension.id,
|
|
91
|
+
version: versionToUse,
|
|
92
|
+
installedVersion: registryExtension.version,
|
|
93
|
+
installedBy: input.installedBy,
|
|
94
|
+
policies: {
|
|
95
|
+
allowOrphanedExecution: false,
|
|
96
|
+
requireSecurityUpdates: true,
|
|
97
|
+
autoRemoveDeprecated: false,
|
|
98
|
+
deprecationGracePeriod: 30,
|
|
99
|
+
...input.policies
|
|
100
|
+
},
|
|
101
|
+
settings: {
|
|
102
|
+
userEnabled: true,
|
|
103
|
+
systemEnabled: true,
|
|
104
|
+
...input.settings
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Fire installation event
|
|
108
|
+
const event = {
|
|
109
|
+
extensionSlug: input.extensionSlug,
|
|
110
|
+
installedBy: input.installedBy,
|
|
111
|
+
installedVersion: installedExtension.installedVersion,
|
|
112
|
+
registryRef: registryExtension.id,
|
|
113
|
+
installedAt: new Date().toISOString(),
|
|
114
|
+
policies: input.policies,
|
|
115
|
+
settings: input.settings,
|
|
116
|
+
tenantId
|
|
117
|
+
};
|
|
118
|
+
this.onExtensionInstalled.fire(event);
|
|
119
|
+
this.logger.info(`Successfully installed extension ${input.extensionSlug}`);
|
|
120
|
+
return installedExtension;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Uninstall an extension
|
|
124
|
+
*/
|
|
125
|
+
async uninstallExtension(orgId, extensionSlug, uninstalledBy, tenantId) {
|
|
126
|
+
this.logger.debug('Uninstalling extension [%s] for org [%s] by user [%s] with tenantId [%s]', extensionSlug, orgId, uninstalledBy, tenantId);
|
|
127
|
+
this.logger.info('Uninstalling extension [%s] in org [%s]', extensionSlug, orgId);
|
|
128
|
+
this.logger.debug('Resolving extension slug [%s] to extension ID', extensionSlug);
|
|
129
|
+
const extensionId = await this.resolveExtensionSlug(extensionSlug);
|
|
130
|
+
this.logger.debug('Extension slug [%s] resolved to ID [%s]', extensionSlug, extensionId);
|
|
131
|
+
if (!extensionId) {
|
|
132
|
+
this.logger.error('Failed to resolve extension slug [%s] - not found in registry', extensionSlug);
|
|
133
|
+
throw new Error(`Extension ${extensionSlug} not found in registry`);
|
|
134
|
+
}
|
|
135
|
+
let extension = null;
|
|
136
|
+
if (extensionId) {
|
|
137
|
+
this.logger.debug('Finding extension by org [%s] and extension ID [%s]', orgId, extensionId);
|
|
138
|
+
// Try to find by extension ID first
|
|
139
|
+
extension = await this.installedExtensionRepository.findByOrgAndExtensionId(orgId, extensionId);
|
|
140
|
+
this.logger.debug('Extension found by ID lookup: [%s]', !!extension);
|
|
141
|
+
}
|
|
142
|
+
// If not found by extension ID, try alternative methods
|
|
143
|
+
if (!extension) {
|
|
144
|
+
this.logger.debug('Extension not found by ID, trying alternative lookup methods');
|
|
145
|
+
// Instead of complex lookups, let's search all installed extensions
|
|
146
|
+
// and match by checking if the registry extension has the matching slug
|
|
147
|
+
const allInstalled = await this.getInstalledExtensions({
|
|
148
|
+
orgId
|
|
149
|
+
});
|
|
150
|
+
this.logger.debug('Found [%d] installed extensions for org [%s]', allInstalled.length, orgId);
|
|
151
|
+
// For each installed extension, check if it matches our slug
|
|
152
|
+
for (const installed of allInstalled) {
|
|
153
|
+
this.logger.debug('Checking installed extension [%j]', {
|
|
154
|
+
id: installed.id,
|
|
155
|
+
extensionId: installed.extension
|
|
156
|
+
});
|
|
157
|
+
// Since we can't easily look up registry by ID, let's try a different approach
|
|
158
|
+
// Use the slug service to find if this matches
|
|
159
|
+
try {
|
|
160
|
+
// Get the extension slug from the registry extension directly if possible
|
|
161
|
+
if (installed.extension) {
|
|
162
|
+
// Try to resolve the extension ID back to slug for comparison
|
|
163
|
+
const extensionIdString = String(installed.extension);
|
|
164
|
+
// Check if this extension ID matches what we'd expect for this slug
|
|
165
|
+
const resolvedExtensionId = await this.resolveExtensionSlug(extensionSlug);
|
|
166
|
+
this.logger.debug('Comparing extension IDs: installed [%s] vs resolved [%s]', extensionIdString, resolvedExtensionId);
|
|
167
|
+
if (resolvedExtensionId && resolvedExtensionId === extensionIdString) {
|
|
168
|
+
this.logger.debug('Found matching extension via alternative lookup');
|
|
169
|
+
extension = installed;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
this.logger.warn('Error in alternative lookup for extension [%s]: [%s]', installed.id, error.message);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!extension) {
|
|
179
|
+
this.logger.error('Extension [%s] not found in org [%s] after all lookup attempts', extensionSlug, orgId);
|
|
180
|
+
throw new Error(`Extension ${extensionSlug} not found in org ${orgId}`);
|
|
181
|
+
}
|
|
182
|
+
this.logger.debug('Found extension to uninstall [%j]', {
|
|
183
|
+
id: extension.id,
|
|
184
|
+
extensionId: extension.extension,
|
|
185
|
+
slug: extension.extensionSlug
|
|
186
|
+
});
|
|
187
|
+
// Use the extension ID from the found extension record
|
|
188
|
+
const actualExtensionId = String(extension.extension);
|
|
189
|
+
this.logger.debug('Using actual extension ID [%s] for uninstall operations', actualExtensionId);
|
|
190
|
+
// Check dependencies - prevent uninstalling if other extensions depend on this
|
|
191
|
+
this.logger.debug('Checking dependency graph for org [%s]', orgId);
|
|
192
|
+
const dependencyGraph = await this.installedExtensionRepository.getDependencyGraph(orgId);
|
|
193
|
+
const extensionDeps = dependencyGraph.dependencies.find(dep => dep.extensionId === actualExtensionId);
|
|
194
|
+
if (extensionDeps && extensionDeps.dependents.length > 0) {
|
|
195
|
+
this.logger.warn('Cannot uninstall extension [%s]: required by dependents [%j]', extensionSlug, extensionDeps.dependents);
|
|
196
|
+
throw new Error(`Cannot uninstall ${extensionSlug}: Required by ${extensionDeps.dependents.join(', ')}`);
|
|
197
|
+
}
|
|
198
|
+
// Delete the extension using the installed extension's ID
|
|
199
|
+
this.logger.debug('Attempting to delete extension [%s] from org [%s]', actualExtensionId, orgId);
|
|
200
|
+
const deleted = await this.installedExtensionRepository.deleteExtension(orgId, actualExtensionId);
|
|
201
|
+
this.logger.debug('Extension deletion result: [%s]', deleted);
|
|
202
|
+
if (deleted) {
|
|
203
|
+
this.logger.debug('Creating uninstall event for extension [%s]', extensionSlug);
|
|
204
|
+
// Fire uninstallation event
|
|
205
|
+
const event = {
|
|
206
|
+
extensionSlug,
|
|
207
|
+
tenantId,
|
|
208
|
+
// Using accountId as tenantId for backward compatibility in events
|
|
209
|
+
uninstalledBy,
|
|
210
|
+
uninstalledAt: new Date().toISOString(),
|
|
211
|
+
wasEnabled: extension.settings?.userEnabled || false,
|
|
212
|
+
dependencies: extension.dependencies || []
|
|
213
|
+
};
|
|
214
|
+
this.logger.debug('Firing uninstall event [%j]', event);
|
|
215
|
+
this.onExtensionUninstalled.fire(event);
|
|
216
|
+
this.logger.info('Successfully uninstalled extension [%s] ([%s])', extensionSlug, actualExtensionId);
|
|
217
|
+
} else {
|
|
218
|
+
this.logger.error('Failed to delete extension [%s] from database', actualExtensionId);
|
|
219
|
+
}
|
|
220
|
+
return deleted;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Update an installed extension's configuration or status
|
|
224
|
+
*/
|
|
225
|
+
async updateInstalledExtension(orgId, extensionSlug, update, tenantId) {
|
|
226
|
+
// For now, use orgId as tenantId for backward compatibility until repository is updated
|
|
227
|
+
this.logger.info(`Updating installed extension ${extensionSlug} for org ${orgId}`);
|
|
228
|
+
const extensionId = await this.resolveExtensionSlug(extensionSlug);
|
|
229
|
+
const updatedExtension = await this.installedExtensionRepository.updateExtension(orgId, extensionId, {
|
|
230
|
+
...update,
|
|
231
|
+
lastUpdatedBy: update.lastUpdatedBy
|
|
232
|
+
});
|
|
233
|
+
if (!updatedExtension) {
|
|
234
|
+
throw new Error(`Extension ${extensionId} not found for org ${orgId}`);
|
|
235
|
+
}
|
|
236
|
+
// Fire update event if status changed
|
|
237
|
+
if (update.status || update.settings) {
|
|
238
|
+
const event = {
|
|
239
|
+
extensionSlug,
|
|
240
|
+
tenantId,
|
|
241
|
+
updatedBy: update.lastUpdatedBy || 'system',
|
|
242
|
+
updatedAt: new Date().toISOString(),
|
|
243
|
+
changes: update
|
|
244
|
+
};
|
|
245
|
+
this.onExtensionUpdated.fire(event);
|
|
246
|
+
}
|
|
247
|
+
return updatedExtension;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get a specific installed extension
|
|
251
|
+
*/
|
|
252
|
+
async getInstalledExtension(orgId, extensionSlug) {
|
|
253
|
+
// For now, use accountId as tenantId for backward compatibility until repository is updated
|
|
254
|
+
const extensionId = await this.resolveExtensionSlug(extensionSlug);
|
|
255
|
+
const result = await this.installedExtensionRepository.findByOrgAndExtensionId(orgId, extensionId);
|
|
256
|
+
return result ? result : null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get all installed extensions for a tenant with optional filtering
|
|
260
|
+
*/
|
|
261
|
+
async getInstalledExtensions(filter) {
|
|
262
|
+
const results = await this.installedExtensionRepository.findExtensions(filter);
|
|
263
|
+
return results;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Enable/disable an installed extension
|
|
267
|
+
*/
|
|
268
|
+
async toggleExtension(orgId, extensionSlug, enabled, toggledBy, tenantId) {
|
|
269
|
+
const activationState = enabled ? ExtensionActivationState.Enabled : ExtensionActivationState.DisabledByUser;
|
|
270
|
+
const updatedExtension = await this.updateInstalledExtension(orgId, extensionSlug, {
|
|
271
|
+
settings: {
|
|
272
|
+
userEnabled: enabled
|
|
273
|
+
},
|
|
274
|
+
runtime: {
|
|
275
|
+
activationState,
|
|
276
|
+
...(enabled ? {
|
|
277
|
+
lastActivated: new Date()
|
|
278
|
+
} : {
|
|
279
|
+
lastDeactivated: new Date()
|
|
280
|
+
})
|
|
281
|
+
},
|
|
282
|
+
lastUpdatedBy: toggledBy
|
|
283
|
+
}, tenantId);
|
|
284
|
+
// Fire toggle event
|
|
285
|
+
const event = enabled ? {
|
|
286
|
+
extensionSlug,
|
|
287
|
+
tenantId,
|
|
288
|
+
enabledBy: toggledBy,
|
|
289
|
+
enabledAt: new Date().toISOString(),
|
|
290
|
+
previousState: updatedExtension.runtime.activationState
|
|
291
|
+
} : {
|
|
292
|
+
extensionSlug,
|
|
293
|
+
tenantId,
|
|
294
|
+
disabledBy: toggledBy,
|
|
295
|
+
disabledAt: new Date().toISOString(),
|
|
296
|
+
reason: 'user'
|
|
297
|
+
};
|
|
298
|
+
// Fire status change event
|
|
299
|
+
if (enabled) {
|
|
300
|
+
this.onExtensionEnabled.fire(event);
|
|
301
|
+
} else {
|
|
302
|
+
this.onExtensionDisabled.fire(event);
|
|
303
|
+
}
|
|
304
|
+
return updatedExtension;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Update extension settings/configuration
|
|
308
|
+
*/
|
|
309
|
+
async updateExtensionSettings(orgId, extensionSlug, userConfig, updatedBy, tenantId) {
|
|
310
|
+
const updatedExtension = await this.updateInstalledExtension(orgId, extensionSlug, {
|
|
311
|
+
settings: {
|
|
312
|
+
userConfig
|
|
313
|
+
},
|
|
314
|
+
lastUpdatedBy: updatedBy
|
|
315
|
+
}, tenantId);
|
|
316
|
+
// Fire configuration update event
|
|
317
|
+
const event = {
|
|
318
|
+
extensionSlug,
|
|
319
|
+
tenantId,
|
|
320
|
+
updatedBy,
|
|
321
|
+
updatedAt: new Date().toISOString(),
|
|
322
|
+
configType: 'user',
|
|
323
|
+
newConfig: userConfig
|
|
324
|
+
// configurationChanges: userConfig,
|
|
325
|
+
// previousConfiguration: updatedExtension.settings.userConfig,
|
|
326
|
+
};
|
|
327
|
+
// Fire configuration update event
|
|
328
|
+
this.onExtensionConfigurationUpdated.fire(event);
|
|
329
|
+
return updatedExtension;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Check for extension updates and return available updates
|
|
333
|
+
*/
|
|
334
|
+
async checkForUpdates(orgId, extensionSlug) {
|
|
335
|
+
const query = {
|
|
336
|
+
orgId
|
|
337
|
+
};
|
|
338
|
+
if (extensionSlug) {
|
|
339
|
+
const extensionId = await this.resolveExtensionSlug(extensionSlug);
|
|
340
|
+
query.extension = extensionId;
|
|
341
|
+
}
|
|
342
|
+
const installedExtensions = await this.installedExtensionRepository.findExtensions(query);
|
|
343
|
+
const updates = [];
|
|
344
|
+
for (const installed of installedExtensions) {
|
|
345
|
+
const registryExtension = await this.registryExtensionService.findExtension(installed.extension?.toString());
|
|
346
|
+
if (registryExtension && registryExtension.version !== installed.installedVersion) {
|
|
347
|
+
updates.push({
|
|
348
|
+
extensionId: installed.extension?.toString(),
|
|
349
|
+
extensionSlug,
|
|
350
|
+
currentVersion: installed.installedVersion,
|
|
351
|
+
availableVersion: registryExtension.version,
|
|
352
|
+
isSecurityUpdate: this.isSecurityUpdate(installed.installedVersion, registryExtension.version)
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return updates;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Update an extension to a newer version
|
|
360
|
+
*/
|
|
361
|
+
async updateExtensionVersion(orgId, extensionSlug, targetVersion, updatedBy, tenantId) {
|
|
362
|
+
const extension = await this.getInstalledExtension(orgId, extensionSlug);
|
|
363
|
+
if (!extension) {
|
|
364
|
+
throw new Error(`Extension ${extensionSlug} not found for organization ${orgId}`);
|
|
365
|
+
}
|
|
366
|
+
// Validate target version exists in registry
|
|
367
|
+
const registryExtension = await this.registryExtensionService.findExtension(extensionSlug);
|
|
368
|
+
if (!registryExtension) {
|
|
369
|
+
throw new Error(`Extension ${extensionSlug} not found in registry`);
|
|
370
|
+
}
|
|
371
|
+
const updatedExtension = await this.updateInstalledExtension(orgId, extensionSlug, {
|
|
372
|
+
lastUpdatedBy: updatedBy
|
|
373
|
+
// Note: Version update should be handled at the model level, not through update input
|
|
374
|
+
}, tenantId);
|
|
375
|
+
// Fire version update event
|
|
376
|
+
const event = {
|
|
377
|
+
extensionSlug,
|
|
378
|
+
updatedBy,
|
|
379
|
+
fromVersion: extension.installedVersion,
|
|
380
|
+
toVersion: targetVersion,
|
|
381
|
+
updatedAt: new Date().toISOString(),
|
|
382
|
+
isSecurityUpdate: this.isSecurityUpdate(extension.installedVersion, targetVersion),
|
|
383
|
+
tenantId
|
|
384
|
+
};
|
|
385
|
+
// Fire version update event
|
|
386
|
+
this.onExtensionVersionUpdated.fire(event);
|
|
387
|
+
return updatedExtension;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Sync installed extensions with registry status
|
|
391
|
+
*/
|
|
392
|
+
async syncWithRegistry(orgId, tenantId) {
|
|
393
|
+
this.logger.info(`Syncing installed extensions with registry for organization ${orgId}`);
|
|
394
|
+
const installedExtensions = await this.installedExtensionRepository.findExtensions({
|
|
395
|
+
orgId
|
|
396
|
+
});
|
|
397
|
+
const syncUpdates = [];
|
|
398
|
+
for (const installed of installedExtensions) {
|
|
399
|
+
const registryExtension = await this.registryExtensionService.findExtension(installed.extension?.toString());
|
|
400
|
+
if (!registryExtension) {
|
|
401
|
+
// Extension removed from registry
|
|
402
|
+
syncUpdates.push({
|
|
403
|
+
orgId,
|
|
404
|
+
extensionId: installed.extension?.toString(),
|
|
405
|
+
registryStatus: 'removed',
|
|
406
|
+
isOrphaned: true
|
|
407
|
+
});
|
|
408
|
+
} else {
|
|
409
|
+
syncUpdates.push({
|
|
410
|
+
extensionId: installed.extension?.toString(),
|
|
411
|
+
registryStatus: registryExtension.status || 'active',
|
|
412
|
+
latestVersion: registryExtension.version,
|
|
413
|
+
isOrphaned: false,
|
|
414
|
+
orgId
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// For now, map to old format for repository compatibility
|
|
419
|
+
const repoCompatibleUpdates = syncUpdates.map(update => ({
|
|
420
|
+
orgId: update.orgId,
|
|
421
|
+
// Use orgId as tenantId for backward compatibility
|
|
422
|
+
extensionId: update.extensionId,
|
|
423
|
+
registryStatus: update.registryStatus,
|
|
424
|
+
latestVersion: update.latestVersion,
|
|
425
|
+
isOrphaned: update.isOrphaned
|
|
426
|
+
}));
|
|
427
|
+
await this.installedExtensionRepository.syncRegistryStatus(repoCompatibleUpdates);
|
|
428
|
+
// Fire sync completion event
|
|
429
|
+
const orphanedUpdates = syncUpdates.filter(u => u.isOrphaned);
|
|
430
|
+
syncUpdates.filter(u => u.registryStatus === 'deprecated');
|
|
431
|
+
const versionsUpdates = syncUpdates.filter(u => u.latestVersion);
|
|
432
|
+
const event = {
|
|
433
|
+
syncedAt: new Date().toISOString(),
|
|
434
|
+
extensionsChecked: syncUpdates.length,
|
|
435
|
+
// syncedExtensions: versionsUpdates.length,
|
|
436
|
+
extensionsOrphaned: orphanedUpdates.length,
|
|
437
|
+
extensionsUpdated: versionsUpdates.length,
|
|
438
|
+
tenantId,
|
|
439
|
+
errors: []
|
|
440
|
+
// orphanedExtensions: orphanedUpdates.map((u) => u.extensionId),
|
|
441
|
+
// deprecatedExtensions: deprecatedUpdates.map((u) => u.extensionId),
|
|
442
|
+
// updatedExtensions: versionsUpdates.map((u) => u.extensionId),
|
|
443
|
+
};
|
|
444
|
+
// Fire sync completed event
|
|
445
|
+
this.onExtensionSyncCompleted.fire(event);
|
|
446
|
+
this.logger.info(`Synced ${syncUpdates.length} extensions with registry`);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get extensions that need attention
|
|
450
|
+
*/
|
|
451
|
+
async getExtensionsNeedingAttention(orgId) {
|
|
452
|
+
const extensions = await this.getInstalledExtensions({
|
|
453
|
+
orgId
|
|
454
|
+
});
|
|
455
|
+
const filtered = extensions.filter(ext => {
|
|
456
|
+
// Check for conditions that need attention
|
|
457
|
+
const isOrphaned = ext.lifecycle?.isOrphaned;
|
|
458
|
+
const isDeprecated = ext.lifecycle?.registryStatus === 'deprecated';
|
|
459
|
+
const hasUpdates = ext.availableVersion && ext.availableVersion !== ext.installedVersion;
|
|
460
|
+
return isOrphaned || isDeprecated || hasUpdates;
|
|
461
|
+
});
|
|
462
|
+
return filtered;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Clean up deprecated or removed extensions based on policies
|
|
466
|
+
*/
|
|
467
|
+
async cleanupExtensions(orgId, tenantId, dryRun) {
|
|
468
|
+
// For now, use accountId as tenantId for backward compatibility until repository is updated
|
|
469
|
+
const result = {
|
|
470
|
+
removed: [],
|
|
471
|
+
deprecated: [],
|
|
472
|
+
warnings: []
|
|
473
|
+
};
|
|
474
|
+
const extensions = await this.installedExtensionRepository.findExtensions({
|
|
475
|
+
orgId
|
|
476
|
+
});
|
|
477
|
+
for (const extension of extensions) {
|
|
478
|
+
const gracePeriodExpired = extension.policies.deprecationGracePeriod > 0 && extension.lifecycle.lastRegistryCheck && Date.now() - new Date(extension.lifecycle.lastRegistryCheck).getTime() > extension.policies.deprecationGracePeriod * 24 * 60 * 60 * 1000;
|
|
479
|
+
// Remove orphaned extensions
|
|
480
|
+
if (extension.lifecycle.isOrphaned && !extension.policies.allowOrphanedExecution) {
|
|
481
|
+
if (!dryRun) {
|
|
482
|
+
await this.uninstallExtension(orgId, extension.extension?.toString(), 'system-cleanup', tenantId);
|
|
483
|
+
}
|
|
484
|
+
result.removed.push(extension.extension?.toString());
|
|
485
|
+
}
|
|
486
|
+
// Remove deprecated extensions if auto-remove is enabled and grace period expired
|
|
487
|
+
else if (extension.lifecycle.registryStatus === 'deprecated' && extension.policies.autoRemoveDeprecated && gracePeriodExpired) {
|
|
488
|
+
if (!dryRun) {
|
|
489
|
+
await this.uninstallExtension(orgId, extension.extension?.toString(), 'system-cleanup', tenantId);
|
|
490
|
+
}
|
|
491
|
+
result.deprecated.push(extension.extension?.toString());
|
|
492
|
+
}
|
|
493
|
+
// Add warnings for extensions that need attention
|
|
494
|
+
else if (extension.lifecycle.registryStatus === 'deprecated' || extension.lifecycle.isOrphaned) {
|
|
495
|
+
result.warnings.push(extension.extension?.toString());
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// Fire cleanup completion event
|
|
499
|
+
const event = {
|
|
500
|
+
tenantId,
|
|
501
|
+
cleanedAt: new Date().toISOString(),
|
|
502
|
+
// cleanupAt: new Date().toISOString(),
|
|
503
|
+
warnings: result.warnings,
|
|
504
|
+
dryRun: false,
|
|
505
|
+
extensionsDeprecated: result.deprecated,
|
|
506
|
+
extensionsRemoved: result.removed
|
|
507
|
+
};
|
|
508
|
+
// Fire cleanup completed event
|
|
509
|
+
this.onExtensionCleanupCompleted.fire(event);
|
|
510
|
+
return result;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Check if update is a security update (simplified logic)
|
|
514
|
+
*/
|
|
515
|
+
isSecurityUpdate(_currentVersion, _targetVersion) {
|
|
516
|
+
// Simple heuristic - in a real implementation, this would check
|
|
517
|
+
// security advisories or version metadata
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-extension-service.js","sources":["../../src/services/installed-extension-service.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=installed-extension-service.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-extension-service.test.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CdmLogger } from '@cdm-logger/core';
|
|
2
|
+
import { BaseService2 } from '@common-stack/store-mongo';
|
|
3
|
+
import { Disposable, DisposableCollection, Emitter } from '@adminide-stack/core';
|
|
4
|
+
import { IRegistryExtensionRepository, IRegistryExtensionModel } from 'common/server';
|
|
5
|
+
export interface AnalyticsEvent {
|
|
6
|
+
eventType: string;
|
|
7
|
+
extensionSlug: string;
|
|
8
|
+
publisherId: string;
|
|
9
|
+
timestamp: string;
|
|
10
|
+
metadata?: string;
|
|
11
|
+
region?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AnalyticsDataPoint {
|
|
15
|
+
timestamp: string;
|
|
16
|
+
value: number;
|
|
17
|
+
secondaryValue?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface VersionMetrics {
|
|
20
|
+
version: string;
|
|
21
|
+
downloads: number;
|
|
22
|
+
installations: number;
|
|
23
|
+
releasedAt: string;
|
|
24
|
+
adoptionRate: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ExtensionMetrics {
|
|
27
|
+
extensionSlug: string;
|
|
28
|
+
name: string;
|
|
29
|
+
version?: string;
|
|
30
|
+
downloads: number;
|
|
31
|
+
installations: number;
|
|
32
|
+
rating?: number;
|
|
33
|
+
reviewCount: number;
|
|
34
|
+
revenue?: number;
|
|
35
|
+
lastUpdated?: string;
|
|
36
|
+
versionMetrics: VersionMetrics[];
|
|
37
|
+
}
|
|
38
|
+
export interface RegionMetrics {
|
|
39
|
+
region: string;
|
|
40
|
+
regionName: string;
|
|
41
|
+
downloads: number;
|
|
42
|
+
installations: number;
|
|
43
|
+
percentage: number;
|
|
44
|
+
}
|
|
45
|
+
export interface PublisherAnalyticsInput {
|
|
46
|
+
publisherId: string;
|
|
47
|
+
period: string;
|
|
48
|
+
extensionSlugs?: string[];
|
|
49
|
+
includeRevenue?: boolean;
|
|
50
|
+
includeRegions?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface PublisherAnalyticsResult {
|
|
53
|
+
publisherId: string;
|
|
54
|
+
totalExtensions: number;
|
|
55
|
+
totalDownloads: number;
|
|
56
|
+
totalInstallations: number;
|
|
57
|
+
averageRating?: number;
|
|
58
|
+
period: string;
|
|
59
|
+
extensions: ExtensionMetrics[];
|
|
60
|
+
downloadTrend: AnalyticsDataPoint[];
|
|
61
|
+
installationTrend: AnalyticsDataPoint[];
|
|
62
|
+
popularRegions: RegionMetrics[];
|
|
63
|
+
}
|
|
64
|
+
export interface RevenueDataPoint {
|
|
65
|
+
timestamp: string;
|
|
66
|
+
revenue: number;
|
|
67
|
+
transactions: number;
|
|
68
|
+
}
|
|
69
|
+
export interface ExtensionRevenue {
|
|
70
|
+
extensionSlug: string;
|
|
71
|
+
name: string;
|
|
72
|
+
revenue: number;
|
|
73
|
+
paidInstallations: number;
|
|
74
|
+
arpu: number;
|
|
75
|
+
}
|
|
76
|
+
export interface PublisherRevenueAnalyticsInput {
|
|
77
|
+
publisherId: string;
|
|
78
|
+
period: string;
|
|
79
|
+
currency?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface PublisherRevenueAnalyticsResult {
|
|
82
|
+
publisherId: string;
|
|
83
|
+
totalRevenue: number;
|
|
84
|
+
currency: string;
|
|
85
|
+
extensionRevenue: ExtensionRevenue[];
|
|
86
|
+
revenueTrend: RevenueDataPoint[];
|
|
87
|
+
topEarningExtensions: ExtensionRevenue[];
|
|
88
|
+
}
|
|
89
|
+
export interface TrackAnalyticsEventInput {
|
|
90
|
+
eventType: string;
|
|
91
|
+
extensionSlug: string;
|
|
92
|
+
userId?: string;
|
|
93
|
+
metadata?: string;
|
|
94
|
+
region?: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
}
|
|
97
|
+
export interface TrackAnalyticsEventResult {
|
|
98
|
+
success: boolean;
|
|
99
|
+
message?: string;
|
|
100
|
+
eventId?: string;
|
|
101
|
+
}
|
|
102
|
+
export interface IPublisherAnalyticsService {
|
|
103
|
+
getPublisherAnalytics(input: PublisherAnalyticsInput): Promise<PublisherAnalyticsResult>;
|
|
104
|
+
getPublisherRevenueAnalytics(input: PublisherRevenueAnalyticsInput): Promise<PublisherRevenueAnalyticsResult>;
|
|
105
|
+
trackAnalyticsEvent(input: TrackAnalyticsEventInput): Promise<TrackAnalyticsEventResult>;
|
|
106
|
+
getRecentAnalyticsEvents(publisherId: string, limit?: number): Promise<AnalyticsEvent[]>;
|
|
107
|
+
}
|
|
108
|
+
export declare class PublisherAnalyticsService extends BaseService2<IRegistryExtensionModel> implements IPublisherAnalyticsService, Disposable {
|
|
109
|
+
private extensionRepository;
|
|
110
|
+
protected readonly onAnalyticsEvent: Emitter<AnalyticsEvent>;
|
|
111
|
+
protected readonly onDownloadEvent: Emitter<AnalyticsEvent>;
|
|
112
|
+
protected readonly onInstallationEvent: Emitter<AnalyticsEvent>;
|
|
113
|
+
protected readonly toDispose: DisposableCollection;
|
|
114
|
+
private logger;
|
|
115
|
+
constructor(extensionRepository: IRegistryExtensionRepository, logger: CdmLogger.ILogger);
|
|
116
|
+
dispose(): void;
|
|
117
|
+
getPublisherAnalytics(input: PublisherAnalyticsInput): Promise<PublisherAnalyticsResult>;
|
|
118
|
+
getPublisherRevenueAnalytics(input: PublisherRevenueAnalyticsInput): Promise<PublisherRevenueAnalyticsResult>;
|
|
119
|
+
trackAnalyticsEvent(input: TrackAnalyticsEventInput): Promise<TrackAnalyticsEventResult>;
|
|
120
|
+
getRecentAnalyticsEvents(publisherId: string, limit?: number): Promise<AnalyticsEvent[]>;
|
|
121
|
+
get analyticsEvents(): import("@adminide-stack/core").Event<AnalyticsEvent>;
|
|
122
|
+
get downloadEvents(): import("@adminide-stack/core").Event<AnalyticsEvent>;
|
|
123
|
+
get installationEvents(): import("@adminide-stack/core").Event<AnalyticsEvent>;
|
|
124
|
+
private generateTrendData;
|
|
125
|
+
private generateRegionData;
|
|
126
|
+
private generateRevenueTrend;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=publisher-analytics-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publisher-analytics-service.d.ts","sourceRoot":"","sources":["../../src/services/publisher-analytics-service.ts"],"names":[],"mappings":"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"}
|