2021-07-07 10:46:50 +02:00
|
|
|
/* eslint-disable prettier/prettier */
|
|
|
|
import { IUnleashConfig } from '../types/option';
|
|
|
|
import { IUnleashStores } from '../types/stores';
|
|
|
|
import { Logger } from '../logger';
|
|
|
|
import BadDataError from '../error/bad-data-error';
|
|
|
|
import { FOREIGN_KEY_VIOLATION } from '../error/db-error';
|
|
|
|
import NameExistsError from '../error/name-exists-error';
|
|
|
|
import { featureMetadataSchema, nameSchema } from '../schema/feature-schema';
|
|
|
|
import {
|
|
|
|
FEATURE_ARCHIVED,
|
2021-08-12 15:04:37 +02:00
|
|
|
FEATURE_CREATED,
|
|
|
|
FEATURE_DELETED,
|
|
|
|
FEATURE_REVIVED,
|
2021-07-07 10:46:50 +02:00
|
|
|
FEATURE_STALE_OFF,
|
|
|
|
FEATURE_STALE_ON,
|
2021-08-12 15:04:37 +02:00
|
|
|
FEATURE_UPDATED,
|
2021-08-26 13:59:11 +02:00
|
|
|
FEATURE_METADATA_UPDATED,
|
2021-07-07 10:46:50 +02:00
|
|
|
} from '../types/events';
|
|
|
|
import { GLOBAL_ENV } from '../types/environment';
|
|
|
|
import NotFoundError from '../error/notfound-error';
|
2021-08-25 13:38:00 +02:00
|
|
|
import {
|
|
|
|
FeatureConfigurationClient,
|
|
|
|
IFeatureStrategiesStore,
|
|
|
|
} from '../types/stores/feature-strategies-store';
|
2021-08-12 15:04:37 +02:00
|
|
|
import { IFeatureTypeStore } from '../types/stores/feature-type-store';
|
|
|
|
import { IEventStore } from '../types/stores/event-store';
|
|
|
|
import { IEnvironmentStore } from '../types/stores/environment-store';
|
|
|
|
import { IProjectStore } from '../types/stores/project-store';
|
|
|
|
import { IFeatureTagStore } from '../types/stores/feature-tag-store';
|
|
|
|
import { IFeatureToggleStore } from '../types/stores/feature-toggle-store';
|
|
|
|
import {
|
|
|
|
FeatureToggle,
|
|
|
|
FeatureToggleDTO,
|
|
|
|
FeatureToggleWithEnvironment,
|
2021-08-26 13:59:11 +02:00
|
|
|
FeatureToggleWithEnvironmentLegacy,
|
2021-08-12 15:04:37 +02:00
|
|
|
IFeatureEnvironmentInfo,
|
|
|
|
IFeatureStrategy,
|
|
|
|
IFeatureToggleQuery,
|
|
|
|
IStrategyConfig,
|
|
|
|
} from '../types/model';
|
|
|
|
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
|
2021-07-07 10:46:50 +02:00
|
|
|
|
|
|
|
class FeatureToggleServiceV2 {
|
|
|
|
private logger: Logger;
|
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private featureStrategiesStore: IFeatureStrategiesStore;
|
|
|
|
|
|
|
|
private featureToggleStore: IFeatureToggleStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private featureTagStore: IFeatureTagStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private featureEnvironmentStore: IFeatureEnvironmentStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private projectStore: IProjectStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private environmentStore: IEnvironmentStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private eventStore: IEventStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
private featureTypeStore: IFeatureTypeStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
{
|
|
|
|
featureStrategiesStore,
|
|
|
|
featureToggleStore,
|
|
|
|
projectStore,
|
|
|
|
eventStore,
|
|
|
|
featureTagStore,
|
|
|
|
environmentStore,
|
|
|
|
featureTypeStore,
|
2021-08-12 15:04:37 +02:00
|
|
|
featureEnvironmentStore,
|
2021-07-07 10:46:50 +02:00
|
|
|
}: Pick<
|
2021-08-25 13:38:00 +02:00
|
|
|
IUnleashStores,
|
|
|
|
| 'featureStrategiesStore'
|
|
|
|
| 'featureToggleStore'
|
|
|
|
| 'projectStore'
|
|
|
|
| 'eventStore'
|
|
|
|
| 'featureTagStore'
|
|
|
|
| 'environmentStore'
|
|
|
|
| 'featureTypeStore'
|
|
|
|
| 'featureEnvironmentStore'
|
2021-07-07 10:46:50 +02:00
|
|
|
>,
|
|
|
|
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
|
|
|
) {
|
|
|
|
this.logger = getLogger('services/feature-toggle-service-v2.ts');
|
|
|
|
this.featureStrategiesStore = featureStrategiesStore;
|
|
|
|
this.featureToggleStore = featureToggleStore;
|
|
|
|
this.featureTagStore = featureTagStore;
|
|
|
|
this.projectStore = projectStore;
|
|
|
|
this.eventStore = eventStore;
|
|
|
|
this.environmentStore = environmentStore;
|
|
|
|
this.featureTypeStore = featureTypeStore;
|
2021-08-12 15:04:37 +02:00
|
|
|
this.featureEnvironmentStore = featureEnvironmentStore;
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
2021-08-26 13:59:11 +02:00
|
|
|
/*
|
|
|
|
TODO after 4.1.0 release:
|
|
|
|
- add FEATURE_STRATEGY_ADD event
|
|
|
|
- add FEATURE_STRATEGY_REMOVE event
|
|
|
|
- add FEATURE_STRATEGY_UPDATE event
|
|
|
|
*/
|
2021-07-07 10:46:50 +02:00
|
|
|
async createStrategy(
|
|
|
|
strategyConfig: Omit<IStrategyConfig, 'id'>,
|
|
|
|
projectName: string,
|
|
|
|
featureName: string,
|
|
|
|
environment: string = GLOBAL_ENV,
|
|
|
|
): Promise<IStrategyConfig> {
|
|
|
|
try {
|
2021-08-25 13:38:00 +02:00
|
|
|
const newFeatureStrategy =
|
|
|
|
await this.featureStrategiesStore.createStrategyConfig({
|
2021-07-07 10:46:50 +02:00
|
|
|
strategyName: strategyConfig.name,
|
|
|
|
constraints: strategyConfig.constraints,
|
|
|
|
parameters: strategyConfig.parameters,
|
|
|
|
projectName,
|
|
|
|
featureName,
|
|
|
|
environment,
|
2021-08-25 13:38:00 +02:00
|
|
|
});
|
2021-07-07 10:46:50 +02:00
|
|
|
return {
|
|
|
|
id: newFeatureStrategy.id,
|
|
|
|
name: newFeatureStrategy.strategyName,
|
|
|
|
constraints: newFeatureStrategy.constraints,
|
|
|
|
parameters: newFeatureStrategy.parameters,
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
if (e.code === FOREIGN_KEY_VIOLATION) {
|
|
|
|
throw new BadDataError(
|
|
|
|
'You have not added the current environment to the project',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* PUT /api/admin/projects/:projectName/features/:featureName/strategies/:strategyId ?
|
|
|
|
* {
|
|
|
|
*
|
|
|
|
* }
|
|
|
|
* @param id
|
|
|
|
* @param updates
|
|
|
|
*/
|
|
|
|
async updateStrategy(
|
|
|
|
id: string,
|
|
|
|
updates: Partial<IFeatureStrategy>,
|
|
|
|
): Promise<IFeatureStrategy> {
|
2021-08-12 15:04:37 +02:00
|
|
|
const exists = await this.featureStrategiesStore.exists(id);
|
2021-07-07 10:46:50 +02:00
|
|
|
if (exists) {
|
|
|
|
return this.featureStrategiesStore.updateStrategy(id, updates);
|
|
|
|
}
|
|
|
|
throw new NotFoundError(`Could not find strategy with id ${id}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
async getStrategiesForEnvironment(
|
|
|
|
projectName: string,
|
|
|
|
featureName: string,
|
|
|
|
environment: string = GLOBAL_ENV,
|
|
|
|
): Promise<IStrategyConfig[]> {
|
2021-08-12 15:04:37 +02:00
|
|
|
const hasEnv = await this.featureEnvironmentStore.featureHasEnvironment(
|
2021-07-07 10:46:50 +02:00
|
|
|
environment,
|
|
|
|
featureName,
|
|
|
|
);
|
|
|
|
if (hasEnv) {
|
2021-08-25 13:38:00 +02:00
|
|
|
const featureStrategies =
|
|
|
|
await this.featureStrategiesStore.getStrategiesForFeature(
|
|
|
|
projectName,
|
|
|
|
featureName,
|
|
|
|
environment,
|
|
|
|
);
|
2021-08-12 15:04:37 +02:00
|
|
|
return featureStrategies.map((strat) => ({
|
2021-07-07 10:46:50 +02:00
|
|
|
id: strat.id,
|
|
|
|
name: strat.strategyName,
|
|
|
|
constraints: strat.constraints,
|
|
|
|
parameters: strat.parameters,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
throw new NotFoundError(
|
|
|
|
`Feature ${featureName} does not have environment ${environment}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GET /api/admin/projects/:projectName/features/:featureName
|
|
|
|
* @param featureName
|
|
|
|
* @param archived - return archived or non archived toggles
|
|
|
|
*/
|
|
|
|
async getFeature(
|
|
|
|
featureName: string,
|
2021-08-12 15:04:37 +02:00
|
|
|
archived: boolean = false,
|
2021-07-07 10:46:50 +02:00
|
|
|
): Promise<FeatureToggleWithEnvironment> {
|
2021-08-25 13:38:00 +02:00
|
|
|
return this.featureStrategiesStore.getFeatureToggleAdmin(
|
|
|
|
featureName,
|
|
|
|
archived,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async getClientFeatures(
|
|
|
|
query?: IFeatureToggleQuery,
|
|
|
|
archived: boolean = false,
|
|
|
|
): Promise<FeatureConfigurationClient[]> {
|
|
|
|
return this.featureStrategiesStore.getFeatures(query, archived, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to retrieve metadata of all feature toggles defined in Unleash.
|
|
|
|
* @param query - Allow you to limit search based on criteria such as project, tags, namePrefix. See @IFeatureToggleQuery
|
|
|
|
* @param archived - Return archived or active toggles
|
|
|
|
* @param includeStrategyId - Include id for strategies
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
async getFeatureToggles(
|
|
|
|
query?: IFeatureToggleQuery,
|
2021-08-12 15:04:37 +02:00
|
|
|
archived: boolean = false,
|
2021-07-07 10:46:50 +02:00
|
|
|
): Promise<FeatureToggle[]> {
|
|
|
|
return this.featureStrategiesStore.getFeatures(query, archived, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
async getFeatureToggle(
|
|
|
|
featureName: string,
|
|
|
|
): Promise<FeatureToggleWithEnvironment> {
|
2021-08-25 13:38:00 +02:00
|
|
|
return this.featureStrategiesStore.getFeatureToggleAdmin(
|
|
|
|
featureName,
|
|
|
|
false,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async createFeatureToggle(
|
|
|
|
projectId: string,
|
|
|
|
value: FeatureToggleDTO,
|
|
|
|
userName: string,
|
|
|
|
): Promise<FeatureToggle> {
|
|
|
|
this.logger.info(`${userName} creates feature toggle ${value.name}`);
|
|
|
|
await this.validateName(value.name);
|
2021-08-12 15:04:37 +02:00
|
|
|
const exists = await this.projectStore.hasProject(projectId);
|
|
|
|
if (exists) {
|
2021-08-25 13:38:00 +02:00
|
|
|
const featureData = await featureMetadataSchema.validateAsync(
|
|
|
|
value,
|
|
|
|
);
|
2021-08-12 15:04:37 +02:00
|
|
|
const createdToggle = await this.featureToggleStore.createFeature(
|
|
|
|
projectId,
|
|
|
|
featureData,
|
|
|
|
);
|
|
|
|
await this.environmentStore.connectFeatureToEnvironmentsForProject(
|
|
|
|
featureData.name,
|
|
|
|
projectId,
|
|
|
|
);
|
2021-08-27 10:15:56 +02:00
|
|
|
|
|
|
|
const data = { featureData, projectId };
|
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
await this.eventStore.store({
|
|
|
|
type: FEATURE_CREATED,
|
|
|
|
createdBy: userName,
|
2021-08-27 10:15:56 +02:00
|
|
|
data,
|
2021-08-12 15:04:37 +02:00
|
|
|
});
|
2021-07-07 10:46:50 +02:00
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
return createdToggle;
|
|
|
|
}
|
|
|
|
throw new NotFoundError(`Project with id ${projectId} does not exist`);
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async updateFeatureToggle(
|
|
|
|
projectId: string,
|
|
|
|
updatedFeature: FeatureToggleDTO,
|
|
|
|
userName: string,
|
|
|
|
): Promise<FeatureToggle> {
|
2021-08-26 13:59:11 +02:00
|
|
|
const featureName = updatedFeature.name;
|
2021-07-07 10:46:50 +02:00
|
|
|
this.logger.info(
|
2021-08-26 13:59:11 +02:00
|
|
|
`${userName} updates feature toggle ${featureName}`,
|
2021-07-07 10:46:50 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
const featureToggle = await this.featureToggleStore.updateFeature(
|
|
|
|
projectId,
|
|
|
|
updatedFeature,
|
|
|
|
);
|
2021-08-26 13:59:11 +02:00
|
|
|
const tags = await this.featureTagStore.getAllTagsForFeature(featureName);
|
|
|
|
|
2021-07-07 10:46:50 +02:00
|
|
|
await this.eventStore.store({
|
2021-08-26 13:59:11 +02:00
|
|
|
type: FEATURE_METADATA_UPDATED,
|
2021-07-07 10:46:50 +02:00
|
|
|
createdBy: userName,
|
|
|
|
data: featureToggle,
|
|
|
|
tags,
|
|
|
|
});
|
|
|
|
return featureToggle;
|
|
|
|
}
|
|
|
|
|
|
|
|
async getFeatureCountForProject(projectId: string): Promise<number> {
|
|
|
|
return this.featureToggleStore.count({
|
|
|
|
archived: false,
|
|
|
|
project: projectId,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async removeAllStrategiesForEnv(
|
|
|
|
toggleName: string,
|
|
|
|
environment: string = GLOBAL_ENV,
|
|
|
|
): Promise<void> {
|
|
|
|
await this.featureStrategiesStore.removeAllStrategiesForEnv(
|
|
|
|
toggleName,
|
|
|
|
environment,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async getStrategy(strategyId: string): Promise<IStrategyConfig> {
|
|
|
|
const strategy = await this.featureStrategiesStore.getStrategyById(
|
|
|
|
strategyId,
|
|
|
|
);
|
|
|
|
return {
|
|
|
|
id: strategy.id,
|
|
|
|
name: strategy.strategyName,
|
|
|
|
constraints: strategy.constraints || [],
|
|
|
|
parameters: strategy.parameters,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async getEnvironmentInfo(
|
|
|
|
project: string,
|
|
|
|
environment: string,
|
|
|
|
featureName: string,
|
|
|
|
): Promise<IFeatureEnvironmentInfo> {
|
2021-08-25 13:38:00 +02:00
|
|
|
const envMetadata =
|
|
|
|
await this.featureEnvironmentStore.getEnvironmentMetaData(
|
|
|
|
environment,
|
|
|
|
featureName,
|
|
|
|
);
|
|
|
|
const strategies =
|
|
|
|
await this.featureStrategiesStore.getStrategiesForFeature(
|
|
|
|
project,
|
|
|
|
featureName,
|
|
|
|
environment,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
return {
|
|
|
|
name: featureName,
|
|
|
|
environment,
|
|
|
|
enabled: envMetadata.enabled,
|
|
|
|
strategies,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteEnvironment(
|
|
|
|
projectId: string,
|
|
|
|
environment: string,
|
|
|
|
): Promise<void> {
|
|
|
|
await this.featureStrategiesStore.deleteConfigurationsForProjectAndEnvironment(
|
|
|
|
projectId,
|
|
|
|
environment,
|
|
|
|
);
|
2021-08-25 13:38:00 +02:00
|
|
|
await this.projectStore.deleteEnvironmentForProject(
|
|
|
|
projectId,
|
|
|
|
environment,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Validations */
|
|
|
|
async validateName(name: string): Promise<string> {
|
|
|
|
await nameSchema.validateAsync({ name });
|
|
|
|
await this.validateUniqueFeatureName(name);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
async validateUniqueFeatureName(name: string): Promise<void> {
|
|
|
|
let msg;
|
|
|
|
try {
|
|
|
|
const feature = await this.featureToggleStore.hasFeature(name);
|
|
|
|
msg = feature.archived
|
|
|
|
? 'An archived toggle with that name already exists'
|
|
|
|
: 'A toggle with that name already exists';
|
|
|
|
} catch (error) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
throw new NameExistsError(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
async hasFeature(name: string): Promise<boolean> {
|
|
|
|
return this.featureToggleStore.exists(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
async updateStale(
|
|
|
|
featureName: string,
|
|
|
|
isStale: boolean,
|
|
|
|
userName: string,
|
|
|
|
): Promise<any> {
|
|
|
|
const feature = await this.featureToggleStore.getFeatureMetadata(
|
|
|
|
featureName,
|
|
|
|
);
|
|
|
|
feature.stale = isStale;
|
|
|
|
await this.featureToggleStore.updateFeature(feature.project, feature);
|
2021-08-26 13:59:11 +02:00
|
|
|
const tags = await this.featureTagStore.getAllTagsForFeature(featureName);
|
|
|
|
const data = await this.getFeatureToggleLegacy(featureName);
|
2021-07-07 10:46:50 +02:00
|
|
|
|
|
|
|
await this.eventStore.store({
|
|
|
|
type: isStale ? FEATURE_STALE_ON : FEATURE_STALE_OFF,
|
|
|
|
createdBy: userName,
|
2021-08-26 13:59:11 +02:00
|
|
|
data,
|
2021-07-07 10:46:50 +02:00
|
|
|
tags,
|
|
|
|
});
|
|
|
|
return feature;
|
|
|
|
}
|
|
|
|
|
|
|
|
async archiveToggle(name: string, userName: string): Promise<void> {
|
|
|
|
await this.featureToggleStore.hasFeature(name);
|
|
|
|
await this.featureToggleStore.archiveFeature(name);
|
2021-08-25 13:38:00 +02:00
|
|
|
const tags =
|
|
|
|
(await this.featureTagStore.getAllTagsForFeature(name)) || [];
|
2021-07-07 10:46:50 +02:00
|
|
|
await this.eventStore.store({
|
|
|
|
type: FEATURE_ARCHIVED,
|
|
|
|
createdBy: userName,
|
|
|
|
data: { name },
|
|
|
|
tags,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async updateEnabled(
|
|
|
|
featureName: string,
|
|
|
|
environment: string,
|
|
|
|
enabled: boolean,
|
|
|
|
userName: string,
|
|
|
|
): Promise<FeatureToggle> {
|
2021-08-25 13:38:00 +02:00
|
|
|
const hasEnvironment =
|
|
|
|
await this.featureEnvironmentStore.featureHasEnvironment(
|
2021-07-07 10:46:50 +02:00
|
|
|
environment,
|
|
|
|
featureName,
|
|
|
|
);
|
2021-08-25 13:38:00 +02:00
|
|
|
if (hasEnvironment) {
|
2021-08-26 13:59:11 +02:00
|
|
|
await this.featureEnvironmentStore.toggleEnvironmentEnabledStatus(
|
2021-08-25 13:38:00 +02:00
|
|
|
environment,
|
|
|
|
featureName,
|
|
|
|
enabled,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
const feature = await this.featureToggleStore.getFeatureMetadata(
|
|
|
|
featureName,
|
|
|
|
);
|
2021-08-26 13:59:11 +02:00
|
|
|
const tags = await this.featureTagStore.getAllTagsForFeature(featureName);
|
|
|
|
const data = await this.getFeatureToggleLegacy(featureName);
|
|
|
|
|
2021-07-07 10:46:50 +02:00
|
|
|
await this.eventStore.store({
|
|
|
|
type: FEATURE_UPDATED,
|
|
|
|
createdBy: userName,
|
2021-08-26 13:59:11 +02:00
|
|
|
data,
|
2021-07-07 10:46:50 +02:00
|
|
|
tags,
|
|
|
|
});
|
|
|
|
return feature;
|
|
|
|
}
|
|
|
|
throw new NotFoundError(
|
|
|
|
`Could not find environment ${environment} for feature: ${featureName}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// @deprecated
|
|
|
|
async toggle(
|
|
|
|
featureName: string,
|
|
|
|
environment: string,
|
|
|
|
userName: string,
|
|
|
|
): Promise<FeatureToggle> {
|
|
|
|
await this.featureToggleStore.hasFeature(featureName);
|
2021-08-25 13:38:00 +02:00
|
|
|
const isEnabled =
|
|
|
|
await this.featureEnvironmentStore.isEnvironmentEnabled(
|
|
|
|
featureName,
|
|
|
|
environment,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
return this.updateEnabled(
|
|
|
|
featureName,
|
|
|
|
environment,
|
|
|
|
!isEnabled,
|
|
|
|
userName,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-26 13:59:11 +02:00
|
|
|
async getFeatureToggleLegacy(featureName: string): Promise<FeatureToggleWithEnvironmentLegacy> {
|
|
|
|
const feature = await this.featureStrategiesStore.getFeatureToggleAdmin(featureName);
|
|
|
|
const globalEnv = feature.environments.find(e => e.name === GLOBAL_ENV);
|
|
|
|
const strategies = globalEnv?.strategies || [];
|
|
|
|
const enabled = globalEnv?.enabled || false;
|
|
|
|
|
|
|
|
return {...feature, enabled, strategies };
|
|
|
|
}
|
|
|
|
|
2021-07-07 10:46:50 +02:00
|
|
|
// @deprecated
|
|
|
|
async updateField(
|
|
|
|
featureName: string,
|
|
|
|
field: string,
|
2021-08-12 15:04:37 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
2021-07-07 10:46:50 +02:00
|
|
|
value: any,
|
|
|
|
userName: string,
|
2021-08-25 13:38:00 +02:00
|
|
|
event?: string,
|
2021-07-07 10:46:50 +02:00
|
|
|
): Promise<any> {
|
|
|
|
const feature = await this.featureToggleStore.getFeatureMetadata(
|
|
|
|
featureName,
|
|
|
|
);
|
|
|
|
feature[field] = value;
|
|
|
|
await this.featureToggleStore.updateFeature(feature.project, feature);
|
2021-08-26 13:59:11 +02:00
|
|
|
const tags = await this.featureTagStore.getAllTagsForFeature(featureName);
|
|
|
|
|
|
|
|
|
|
|
|
// Workaround to support pre 4.1 format
|
|
|
|
const data = await this.getFeatureToggleLegacy(featureName);
|
2021-07-07 10:46:50 +02:00
|
|
|
|
|
|
|
await this.eventStore.store({
|
2021-08-25 13:38:00 +02:00
|
|
|
type: event || FEATURE_UPDATED,
|
2021-07-07 10:46:50 +02:00
|
|
|
createdBy: userName,
|
2021-08-26 13:59:11 +02:00
|
|
|
data,
|
2021-07-07 10:46:50 +02:00
|
|
|
tags,
|
|
|
|
});
|
|
|
|
return feature;
|
|
|
|
}
|
|
|
|
|
|
|
|
async getArchivedFeatures(): Promise<FeatureToggle[]> {
|
|
|
|
return this.getFeatureToggles({}, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteFeature(featureName: string, userName: string): Promise<void> {
|
2021-08-12 15:04:37 +02:00
|
|
|
await this.featureToggleStore.delete(featureName);
|
2021-07-07 10:46:50 +02:00
|
|
|
await this.eventStore.store({
|
|
|
|
type: FEATURE_DELETED,
|
|
|
|
createdBy: userName,
|
|
|
|
data: {
|
2021-08-12 15:04:37 +02:00
|
|
|
featureName,
|
2021-07-07 10:46:50 +02:00
|
|
|
},
|
2021-08-12 15:04:37 +02:00
|
|
|
});
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async reviveToggle(featureName: string, userName: string): Promise<void> {
|
|
|
|
const data = await this.featureToggleStore.reviveFeature(featureName);
|
2021-08-25 13:38:00 +02:00
|
|
|
const tags = await this.featureTagStore.getAllTagsForFeature(
|
|
|
|
featureName,
|
|
|
|
);
|
2021-07-07 10:46:50 +02:00
|
|
|
await this.eventStore.store({
|
|
|
|
type: FEATURE_REVIVED,
|
|
|
|
createdBy: userName,
|
|
|
|
data,
|
2021-08-12 15:04:37 +02:00
|
|
|
tags,
|
2021-07-07 10:46:50 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-08-25 13:38:00 +02:00
|
|
|
async getMetadataForAllFeatures(
|
|
|
|
archived: boolean,
|
|
|
|
): Promise<FeatureToggle[]> {
|
2021-07-07 10:46:50 +02:00
|
|
|
return this.featureToggleStore.getFeatures(archived);
|
|
|
|
}
|
|
|
|
|
2021-08-12 15:04:37 +02:00
|
|
|
async getProjectId(name: string): Promise<string> {
|
2021-08-25 13:38:00 +02:00
|
|
|
const { project } = await this.featureToggleStore.getFeatureMetadata(
|
|
|
|
name,
|
|
|
|
);
|
2021-08-12 15:04:37 +02:00
|
|
|
return project;
|
|
|
|
}
|
2021-07-07 10:46:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = FeatureToggleServiceV2;
|
|
|
|
export default FeatureToggleServiceV2;
|