UNPKG

unleash-server

Version:

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

123 lines 5.17 kB
import NotFoundError from '../error/notfound-error.js'; import { FEATURE_TAGGED, FEATURE_UNTAGGED, TAG_CREATED, } from '../events/index.js'; import { tagSchema } from './tag-schema.js'; import { FeatureTaggedEvent, } from '../types/index.js'; import { BadDataError, FOREIGN_KEY_VIOLATION } from '../../lib/error/index.js'; class FeatureTagService { constructor({ tagStore, featureTagStore, featureToggleStore, }, { getLogger }, eventService) { this.logger = getLogger('/services/feature-tag-service.ts'); this.tagStore = tagStore; this.featureTagStore = featureTagStore; this.featureToggleStore = featureToggleStore; this.eventService = eventService; } async listTags(featureName) { return this.featureTagStore.getAllTagsForFeature(featureName); } async listFeatures(tagValue) { return this.featureTagStore.getAllFeaturesForTag(tagValue); } // TODO: add project Id async addTag(featureName, tag, auditUser) { this.logger.debug(`Adding tag ${tag} to {featureName}`); const featureToggle = await this.featureToggleStore.get(featureName); if (featureToggle === undefined) { throw new NotFoundError(); } const validatedTag = await tagSchema.validateAsync(tag); await this.createTagIfNeeded(validatedTag, auditUser); await this.featureTagStore.tagFeature(featureName, validatedTag, auditUser.id); await this.eventService.storeEvent(new FeatureTaggedEvent({ featureName, project: featureToggle.project, data: validatedTag, auditUser, })); return validatedTag; } async updateTags(featureNames, addedTags, removedTags, auditUser) { const featureToggles = await this.featureToggleStore.getAllByNames(featureNames); await Promise.all(addedTags.map((tag) => this.createTagIfNeeded(tag, auditUser))); const createdFeatureTags = featureNames.flatMap((featureName) => addedTags.map((addedTag) => ({ featureName, tagType: addedTag.type, tagValue: addedTag.value, createdByUserId: auditUser.id, }))); await this.featureTagStore.tagFeatures(createdFeatureTags); const removedFeatureTags = featureNames.flatMap((featureName) => removedTags.map((addedTag) => ({ featureName, tagType: addedTag.type, tagValue: addedTag.value, }))); await this.featureTagStore.untagFeatures(removedFeatureTags); const creationEvents = featureToggles.flatMap((featureToggle) => addedTags.map((addedTag) => ({ type: FEATURE_TAGGED, createdBy: auditUser.username, featureName: featureToggle.name, project: featureToggle.project, data: addedTag, createdByUserId: auditUser.id, ip: auditUser.ip, }))); const removalEvents = featureToggles.flatMap((featureToggle) => removedTags.map((removedTag) => ({ type: FEATURE_UNTAGGED, featureName: featureToggle.name, project: featureToggle.project, preData: removedTag, createdBy: auditUser.username, createdByUserId: auditUser.id, ip: auditUser.ip, }))); await this.eventService.storeEvents([ ...creationEvents, ...removalEvents, ]); } async createTagIfNeeded(tag, auditUser) { try { await this.tagStore.getTag(tag.type, tag.value); } catch (error) { if (error instanceof NotFoundError) { try { await this.tagStore.createTag(tag); await this.eventService.storeEvent({ type: TAG_CREATED, createdBy: auditUser.username, createdByUserId: auditUser.id, ip: auditUser.ip, data: tag, }); } catch (err) { if (err.code === FOREIGN_KEY_VIOLATION) { throw new BadDataError(`Tag type '${tag.type}' does not exist`); } } } } } // TODO: add project Id async removeTag(featureName, tag, auditUser) { const featureToggle = await this.featureToggleStore.get(featureName); if (featureToggle === undefined) { /// No toggle, so no point in removing tags return; } const tags = await this.featureTagStore.getAllTagsForFeature(featureName); await this.featureTagStore.untagFeature(featureName, tag); await this.eventService.storeEvent({ type: FEATURE_UNTAGGED, createdBy: auditUser.username, createdByUserId: auditUser.id, ip: auditUser.ip, featureName, project: featureToggle.project, preData: tag, tags, }); } } export default FeatureTagService; //# sourceMappingURL=feature-tag-service.js.map