UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

199 lines • 9.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("../../../types/events"); const schema_1 = require("../shared/schema"); const schema_2 = require("../shared/schema"); const types_1 = require("../../../types"); const util_1 = require("../../../util"); const findOutdatedSdks_1 = require("./findOutdatedSdks"); const metric_events_1 = require("../../../metric-events"); const error_1 = require("../../../error"); class ClientInstanceService { constructor({ clientMetricsStoreV2, strategyStore, featureToggleStore, clientInstanceStore, clientApplicationsStore, eventStore, }, { getLogger, flagResolver, eventBus, }, privateProjectChecker) { this.apps = {}; this.seenClients = {}; this.clientMetricsStoreV2 = clientMetricsStoreV2; this.strategyStore = strategyStore; this.featureToggleStore = featureToggleStore; this.clientApplicationsStore = clientApplicationsStore; this.clientInstanceStore = clientInstanceStore; this.eventStore = eventStore; this.eventBus = eventBus; this.privateProjectChecker = privateProjectChecker; this.flagResolver = flagResolver; this.logger = getLogger('/services/client-metrics/client-instance-service.ts'); } async registerInstance(data, clientIp) { const value = await schema_2.clientMetricsSchema.validateAsync(data); await this.clientInstanceStore.setLastSeen({ appName: value.appName, instanceId: value.instanceId, environment: value.environment, clientIp: clientIp, }); } async registerClient(data, clientIp) { const value = await schema_1.clientRegisterSchema.validateAsync(data); value.clientIp = clientIp; value.createdBy = types_1.SYSTEM_USER.username; this.seenClients[this.clientKey(value)] = value; this.eventBus.emit(metric_events_1.CLIENT_REGISTERED, value); if (value.sdkVersion && value.sdkVersion.indexOf(':') > -1) { const [sdkName, sdkVersion] = value.sdkVersion.split(':'); const heartbeatEvent = { sdkName, sdkVersion, metadata: { platformName: data.platformName, platformVersion: data.platformVersion, yggdrasilVersion: data.yggdrasilVersion, specVersion: data.specVersion, }, }; this.eventStore.emit(events_1.CLIENT_REGISTER, heartbeatEvent); } } async announceUnannounced() { if (this.clientApplicationsStore) { const appsToAnnounce = await this.clientApplicationsStore.setUnannouncedToAnnounced(); if (appsToAnnounce.length > 0) { const events = appsToAnnounce.map((app) => ({ type: events_1.APPLICATION_CREATED, createdBy: app.createdBy || types_1.SYSTEM_USER.username, data: app, createdByUserId: app.createdByUserId || types_1.SYSTEM_USER.id, ip: '', // TODO: fix this, how do we get the ip from the client? This comes from a row in the DB })); await this.eventStore.batchStore(events); } } } clientKey(client) { return `${client.appName}_${client.instanceId}`; } async bulkAdd() { if (this && this.seenClients && this.clientApplicationsStore && this.clientInstanceStore) { const uniqueRegistrations = Object.values(this.seenClients); const uniqueApps = Object.values(uniqueRegistrations.reduce((soFar, reg) => { // eslint-disable-next-line no-param-reassign soFar[reg.appName] = reg; return soFar; }, {})); this.seenClients = {}; try { if (uniqueRegistrations.length > 0) { await this.clientApplicationsStore.bulkUpsert(uniqueApps); await this.clientInstanceStore.bulkUpsert(uniqueRegistrations); } } catch (err) { this.logger.warn('Failed to register clients', err); } } } async getApplications(query, userId) { const applications = await this.clientApplicationsStore.getApplications({ ...query, sortBy: query.sortBy || 'appName' }); const accessibleProjects = await this.privateProjectChecker.getUserAccessibleProjects(userId); if (accessibleProjects.mode === 'all') { return applications; } else { return { applications: applications.applications.map((application) => { return { ...application, usage: application.usage?.filter((usageItem) => usageItem.project === util_1.ALL_PROJECTS || accessibleProjects.projects.includes(usageItem.project)), }; }), total: applications.total, }; } } async getApplication(appName) { const [seenToggles, application, instances, strategies, features] = await Promise.all([ this.clientMetricsStoreV2.getSeenTogglesForApp(appName), this.clientApplicationsStore.get(appName), this.clientInstanceStore.getByAppName(appName), this.strategyStore.getAll(), this.featureToggleStore.getAll(), ]); if (application === undefined) { throw new error_1.NotFoundError(`Could not find application with appName ${appName}`); } return { appName: application.appName, createdAt: application.createdAt, description: application.description, url: application.url, color: application.color, icon: application.icon, strategies: application.strategies.map((name) => { const found = strategies.find((f) => f.name === name); return found || { name, notFound: true }; }), instances, seenToggles: seenToggles.map((name) => { const found = features.find((f) => f.name === name); return found || { name, notFound: true }; }), links: { self: `/api/applications/${application.appName}`, }, }; } async getApplicationOverview(appName, userId) { const result = await this.clientApplicationsStore.getApplicationOverview(appName); const accessibleProjects = await this.privateProjectChecker.filterUserAccessibleProjects(userId, result.projects); result.projects = accessibleProjects; result.environments.forEach((environment) => { environment.issues.outdatedSdks = (0, findOutdatedSdks_1.findOutdatedSDKs)(environment.sdks); }); return result; } async getRecentApplicationEnvironmentInstances(appName, environment) { const instances = await this.clientInstanceStore.getRecentByAppNameAndEnvironment(appName, environment); return instances.map((instance) => ({ instanceId: instance.instanceId, clientIp: instance.clientIp, sdkVersion: instance.sdkVersion, lastSeen: instance.lastSeen, })); } async deleteApplication(appName) { await this.clientInstanceStore.deleteForApplication(appName); await this.clientApplicationsStore.delete(appName); } async createApplication(input) { await this.clientApplicationsStore.upsert(input); } async removeInstancesOlderThanTwoDays() { return this.clientInstanceStore.removeInstancesOlderThanTwoDays(); } async getOutdatedSdks() { const sdkApps = await this.clientInstanceStore.groupApplicationsBySdk(); return sdkApps.filter((sdkApp) => (0, findOutdatedSdks_1.isOutdatedSdk)(sdkApp.sdkVersion)); } async getOutdatedSdksByProject(projectId) { const sdkApps = await this.clientInstanceStore.groupApplicationsBySdkAndProject(projectId); return sdkApps.filter((sdkApp) => (0, findOutdatedSdks_1.isOutdatedSdk)(sdkApp.sdkVersion)); } async usesSdkOlderThan(sdkName, sdkVersion) { const semver = (0, util_1.parseStrictSemVer)(sdkVersion); const instancesOfSdk = await this.clientInstanceStore.getBySdkName(sdkName); return instancesOfSdk.some((instance) => { if (instance.sdkVersion) { const [_sdkName, sdkVersion] = instance.sdkVersion.split(':'); const instanceUsedSemver = (0, util_1.parseStrictSemVer)(sdkVersion); return (instanceUsedSemver !== null && semver !== null && instanceUsedSemver < semver); } }); } } exports.default = ClientInstanceService; //# sourceMappingURL=instance-service.js.map