unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
146 lines (143 loc) • 7.34 kB
JavaScript
import Controller from '../../controller.js';
import { NONE, UPDATE_FEATURE_ENVIRONMENT_VARIANTS, } from '../../../types/permissions.js';
import { WeightType } from '../../../types/model.js';
import { createRequestSchema } from '../../../openapi/util/create-request-schema.js';
import { createResponseSchema } from '../../../openapi/util/create-response-schema.js';
import { BadDataError, PermissionError } from '../../../../lib/error/index.js';
import { getStandardResponses } from '../../../openapi/index.js';
const PREFIX = '/:projectId/features/:featureName/variants';
const ENV_PREFIX = '/:projectId/features/:featureName/environments/:environment/variants';
export default class VariantsController extends Controller {
constructor(config, { featureToggleService, openApiService, accessService, }) {
super(config);
this.logger = config.getLogger('admin-api/project/variants.ts');
this.featureService = featureToggleService;
this.accessService = accessService;
this.route({
method: 'get',
path: ENV_PREFIX,
permission: NONE,
handler: this.getVariantsOnEnv,
middleware: [
openApiService.validPath({
summary: 'Get variants for a feature in an environment',
description: `Returns the variants for a feature in a specific environment. If the feature has no variants it will return an empty array of variants`,
tags: ['Features'],
operationId: 'getEnvironmentFeatureVariants',
responses: {
200: createResponseSchema('featureVariantsSchema'),
...getStandardResponses(401, 403, 404),
},
}),
],
});
this.route({
method: 'patch',
path: ENV_PREFIX,
permission: UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
handler: this.patchVariantsOnEnv,
middleware: [
openApiService.validPath({
summary: "Patch a feature's variants in an environment",
description: `Apply a list of patches to the features environments in the specified environment. The patch objects should conform to the [JSON-patch format (RFC 6902)](https://www.rfc-editor.org/rfc/rfc6902).`,
tags: ['Features'],
operationId: 'patchEnvironmentsFeatureVariants',
requestBody: createRequestSchema('patchesSchema'),
responses: {
200: createResponseSchema('featureVariantsSchema'),
...getStandardResponses(400, 401, 403, 404),
},
}),
],
});
this.route({
method: 'put',
path: ENV_PREFIX,
permission: UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
handler: this.overwriteVariantsOnEnv,
middleware: [
openApiService.validPath({
summary: 'Create (overwrite) variants for a feature in an environment',
description: `This overwrites the current variants for the feature flag in the :featureName parameter for the :environment parameter.
The backend will validate the input for the following invariants:
* If there are variants, there needs to be at least one variant with \`weightType: variable\`
* The sum of the weights of variants with \`weightType: fix\` must be strictly less than 1000 (< 1000)
The backend will also distribute remaining weight up to 1000 after adding the variants with \`weightType: fix\` together amongst the variants of \`weightType: variable\``,
tags: ['Features'],
operationId: 'overwriteEnvironmentFeatureVariants',
requestBody: createRequestSchema('variantsSchema'),
responses: {
200: createResponseSchema('featureVariantsSchema'),
...getStandardResponses(400, 401, 403),
},
}),
],
});
this.route({
method: 'put',
path: `${PREFIX}-batch`,
permission: NONE,
handler: this.pushVariantsToEnvironments,
middleware: [
openApiService.validPath({
tags: ['Features'],
operationId: 'overwriteFeatureVariantsOnEnvironments',
summary: 'Create (overwrite) variants for a feature flag in multiple environments',
description: 'This overwrites the current variants for the feature flag in the :featureName parameter for the :environment parameter.',
requestBody: createRequestSchema('pushVariantsSchema'),
responses: {
200: createResponseSchema('featureVariantsSchema'),
...getStandardResponses(400, 401, 403),
},
}),
],
});
}
async pushVariantsToEnvironments(req, res) {
const { projectId, featureName } = req.params;
const { environments, variants } = req.body;
if (environments === undefined || environments.length === 0) {
throw new BadDataError('No environments provided');
}
await this.checkAccess(req.user, projectId, environments, UPDATE_FEATURE_ENVIRONMENT_VARIANTS);
const variantsWithDefaults = (variants || []).map((variant) => ({
weightType: WeightType.VARIABLE,
stickiness: 'default',
...variant,
}));
await this.featureService.crProtectedSetVariantsOnEnvs(projectId, featureName, environments, variantsWithDefaults, req.user, req.audit);
res.status(200).json({
version: 1,
variants: variantsWithDefaults,
});
}
async checkAccess(user, projectId, environments, permission) {
for (const environment of environments) {
if (!(await this.accessService.hasPermission(user, permission, projectId, environment))) {
throw new PermissionError(UPDATE_FEATURE_ENVIRONMENT_VARIANTS, environment);
}
}
}
async getVariantsOnEnv(req, res) {
const { featureName, environment } = req.params;
const variants = await this.featureService.getVariantsForEnv(featureName, environment);
res.status(200).json({ version: 1, variants: variants || [] });
}
async patchVariantsOnEnv(req, res) {
const { projectId, featureName, environment } = req.params;
const variants = await this.featureService.updateVariantsOnEnv(featureName, projectId, environment, req.body, req.user, req.audit);
res.status(200).json({
version: 1,
variants,
});
}
async overwriteVariantsOnEnv(req, res) {
const { featureName, environment, projectId } = req.params;
const variants = await this.featureService.crProtectedSaveVariantsOnEnv(projectId, featureName, environment, req.body, req.user, req.audit);
res.status(200).json({
version: 1,
variants: variants,
});
}
}
//# sourceMappingURL=variants.js.map