UNPKG

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
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