unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
123 lines • 5.17 kB
JavaScript
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