UNPKG

unleash-server

Version:

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

249 lines • 11.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const controller_1 = __importDefault(require("../controller")); const extract_user_1 = require("../../util/extract-user"); const permissions_1 = require("../../types/permissions"); const feature_schema_1 = require("../../schema/feature-schema"); const constants_1 = require("../../util/constants"); const features_schema_1 = require("../../openapi/spec/features-schema"); const serialize_dates_1 = require("../../types/serialize-dates"); const create_request_schema_1 = require("../../openapi/util/create-request-schema"); const create_response_schema_1 = require("../../openapi/util/create-response-schema"); const standard_responses_1 = require("../../openapi/util/standard-responses"); const version = 1; class FeatureController extends controller_1.default { constructor(config, { featureTagService, featureToggleServiceV2, openApiService, }) { super(config); this.tagService = featureTagService; this.openApiService = openApiService; this.service = featureToggleServiceV2; if (!config.disableLegacyFeaturesApi) { this.post('/', this.createToggle, permissions_1.CREATE_FEATURE); this.get('/:featureName', this.getToggle); this.put('/:featureName', this.updateToggle, permissions_1.UPDATE_FEATURE); this.delete('/:featureName', this.archiveToggle, permissions_1.DELETE_FEATURE); this.post('/:featureName/toggle', this.toggle, permissions_1.UPDATE_FEATURE); this.post('/:featureName/toggle/on', this.toggleOn, permissions_1.UPDATE_FEATURE); this.post('/:featureName/toggle/off', this.toggleOff, permissions_1.UPDATE_FEATURE); this.post('/:featureName/stale/on', this.staleOn, permissions_1.UPDATE_FEATURE); this.post('/:featureName/stale/off', this.staleOff, permissions_1.UPDATE_FEATURE); } this.route({ method: 'get', path: '', handler: this.getAllToggles, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'getAllToggles', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featuresSchema') }, deprecated: true, }), ], }); this.route({ method: 'post', path: '/validate', handler: this.validate, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'validateFeature', responses: { 200: standard_responses_1.emptyResponse }, }), ], }); this.route({ method: 'get', path: '/:featureName/tags', handler: this.listTags, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'listTags', responses: { 200: (0, create_response_schema_1.createResponseSchema)('tagsSchema') }, }), ], }); this.route({ method: 'post', path: '/:featureName/tags', permission: permissions_1.UPDATE_FEATURE, handler: this.addTag, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'addTag', requestBody: (0, create_request_schema_1.createRequestSchema)('tagSchema'), responses: { 201: (0, create_response_schema_1.resourceCreatedResponseSchema)('tagSchema'), }, }), ], }); this.route({ method: 'delete', path: '/:featureName/tags/:type/:value', permission: permissions_1.UPDATE_FEATURE, acceptAnyContentType: true, handler: this.removeTag, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'removeTag', responses: { 200: standard_responses_1.emptyResponse }, }), ], }); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types paramToArray(param) { if (!param) { return param; } return Array.isArray(param) ? param : [param]; } async prepQuery({ tag, project, namePrefix, }) { if (!tag && !project && !namePrefix) { return null; } const tagQuery = this.paramToArray(tag); const projectQuery = this.paramToArray(project); const query = await feature_schema_1.querySchema.validateAsync({ tag: tagQuery, project: projectQuery, namePrefix, }); if (query.tag) { query.tag = query.tag.map((q) => q.split(':')); } return query; } async getAllToggles(req, res) { const query = await this.prepQuery(req.query); const { user } = req; const features = await this.service.getFeatureToggles(query, user.id); this.openApiService.respondWithValidation(200, res, features_schema_1.featuresSchema.$id, { version, features: (0, serialize_dates_1.serializeDates)(features) }); } async getToggle(req, res) { const name = req.params.featureName; const feature = await this.service.getFeatureToggleLegacy(name); res.json(feature).end(); } async listTags(req, res) { const tags = await this.tagService.listTags(req.params.featureName); res.json({ version, tags }); } async addTag(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const tag = await this.tagService.addTag(featureName, req.body, userName); res.status(201).header('location', `${featureName}/tags`).json(tag); } // TODO async removeTag(req, res) { const { featureName, type, value } = req.params; const userName = (0, extract_user_1.extractUsername)(req); await this.tagService.removeTag(featureName, { type, value }, userName); res.status(200).end(); } async validate(req, res) { const { name } = req.body; await this.service.validateName(name); res.status(200).end(); } async createToggle(req, res) { const userName = (0, extract_user_1.extractUsername)(req); const toggle = req.body; const validatedToggle = await feature_schema_1.featureSchema.validateAsync(toggle); const { enabled, project, name, variants = [] } = validatedToggle; const createdFeature = await this.service.createFeatureToggle(project, validatedToggle, userName, true); const strategies = await Promise.all((toggle.strategies ?? []).map(async (s) => this.service.createStrategy(s, { projectId: project, featureName: name, environment: constants_1.DEFAULT_ENV, }, userName, req.user))); await this.service.updateEnabled(project, name, constants_1.DEFAULT_ENV, enabled, userName); await this.service.saveVariants(name, project, variants, userName); res.status(201).json({ ...createdFeature, variants, enabled, strategies, }); } async updateToggle(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const updatedFeature = req.body; updatedFeature.name = featureName; const projectId = await this.service.getProjectId(featureName); const value = await feature_schema_1.featureSchema.validateAsync(updatedFeature); await this.service.updateFeatureToggle(projectId, value, userName, featureName); await this.service.removeAllStrategiesForEnv(featureName); if (updatedFeature.strategies) { await Promise.all(updatedFeature.strategies.map(async (s) => this.service.createStrategy(s, { projectId, featureName, environment: constants_1.DEFAULT_ENV }, userName, req.user))); } await this.service.updateEnabled(projectId, featureName, constants_1.DEFAULT_ENV, updatedFeature.enabled, userName); await this.service.saveVariants(featureName, projectId, value.variants || [], userName); const feature = await this.service.storeFeatureUpdatedEventLegacy(featureName, userName); res.status(200).json(feature); } /** * @deprecated TODO: remove? * * Kept to keep backward compatibility */ async toggle(req, res) { const userName = (0, extract_user_1.extractUsername)(req); const { featureName } = req.params; const projectId = await this.service.getProjectId(featureName); const feature = await this.service.toggle(projectId, featureName, constants_1.DEFAULT_ENV, userName); await this.service.storeFeatureUpdatedEventLegacy(featureName, userName); res.status(200).json(feature); } async toggleOn(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const projectId = await this.service.getProjectId(featureName); const feature = await this.service.updateEnabled(projectId, featureName, constants_1.DEFAULT_ENV, true, userName); await this.service.storeFeatureUpdatedEventLegacy(featureName, userName); res.json(feature); } async toggleOff(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const projectId = await this.service.getProjectId(featureName); const feature = await this.service.updateEnabled(projectId, featureName, constants_1.DEFAULT_ENV, false, userName); await this.service.storeFeatureUpdatedEventLegacy(featureName, userName); res.json(feature); } async staleOn(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); await this.service.updateStale(featureName, true, userName); const feature = await this.service.getFeatureToggleLegacy(featureName); res.json(feature); } async staleOff(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); await this.service.updateStale(featureName, false, userName); const feature = await this.service.getFeatureToggleLegacy(featureName); res.json(feature); } async archiveToggle(req, res) { const { featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); await this.service.archiveToggle(featureName, userName); res.status(200).end(); } } exports.default = FeatureController; //# sourceMappingURL=feature.js.map