UNPKG

unleash-server

Version:

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

430 lines • 20.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fast_json_patch_1 = require("fast-json-patch"); const controller_1 = __importDefault(require("../../controller")); const permissions_1 = require("../../../types/permissions"); const extract_user_1 = require("../../../util/extract-user"); const feature_schema_1 = require("../../../openapi/spec/feature-schema"); 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 PATH = '/:projectId/features'; const PATH_FEATURE = `${PATH}/:featureName`; const PATH_FEATURE_CLONE = `${PATH_FEATURE}/clone`; const PATH_ENV = `${PATH_FEATURE}/environments/:environment`; const PATH_STRATEGIES = `${PATH_ENV}/strategies`; const PATH_STRATEGY = `${PATH_STRATEGIES}/:strategyId`; class ProjectFeaturesController extends controller_1.default { constructor(config, { featureToggleServiceV2, openApiService, segmentService, }) { super(config); this.featureService = featureToggleServiceV2; this.openApiService = openApiService; this.segmentService = segmentService; this.logger = config.getLogger('/admin-api/project/features.ts'); this.route({ method: 'get', path: PATH_ENV, permission: permissions_1.NONE, handler: this.getFeatureEnvironment, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'getFeatureEnvironment', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureEnvironmentSchema'), }, }), ], }); this.route({ method: 'post', path: `${PATH_ENV}/off`, acceptAnyContentType: true, handler: this.toggleFeatureEnvironmentOff, permission: permissions_1.UPDATE_FEATURE_ENVIRONMENT, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'toggleFeatureEnvironmentOff', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'post', path: `${PATH_ENV}/on`, acceptAnyContentType: true, handler: this.toggleFeatureEnvironmentOn, permission: permissions_1.UPDATE_FEATURE_ENVIRONMENT, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'toggleFeatureEnvironmentOn', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'get', path: PATH_STRATEGIES, handler: this.getFeatureStrategies, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'getFeatureStrategies', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureStrategySchema'), }, }), ], }); this.route({ method: 'post', path: PATH_STRATEGIES, handler: this.addFeatureStrategy, permission: permissions_1.CREATE_FEATURE_STRATEGY, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'addFeatureStrategy', requestBody: (0, create_request_schema_1.createRequestSchema)('createFeatureStrategySchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureStrategySchema'), }, }), ], }); this.route({ method: 'get', path: PATH_STRATEGY, handler: this.getFeatureStrategy, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'getFeatureStrategy', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureStrategySchema'), }, }), ], }); this.route({ method: 'post', path: `${PATH_STRATEGIES}/set-sort-order`, handler: this.setStrategiesSortOrder, permission: permissions_1.UPDATE_FEATURE_STRATEGY, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'setStrategySortOrder', requestBody: (0, create_request_schema_1.createRequestSchema)('setStrategySortOrderSchema'), responses: { 200: standard_responses_1.emptyResponse, }, }), ], }); this.route({ method: 'put', path: PATH_STRATEGY, handler: this.updateFeatureStrategy, permission: permissions_1.UPDATE_FEATURE_STRATEGY, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'updateFeatureStrategy', requestBody: (0, create_request_schema_1.createRequestSchema)('updateFeatureStrategySchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureStrategySchema'), }, }), ], }); this.route({ method: 'patch', path: PATH_STRATEGY, handler: this.patchFeatureStrategy, permission: permissions_1.UPDATE_FEATURE_STRATEGY, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'patchFeatureStrategy', requestBody: (0, create_request_schema_1.createRequestSchema)('patchesSchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureStrategySchema'), }, }), ], }); this.route({ method: 'delete', path: PATH_STRATEGY, acceptAnyContentType: true, handler: this.deleteFeatureStrategy, permission: permissions_1.DELETE_FEATURE_STRATEGY, middleware: [ openApiService.validPath({ operationId: 'deleteFeatureStrategy', tags: ['Features'], responses: { 200: standard_responses_1.emptyResponse }, }), ], }); this.route({ method: 'get', path: PATH, handler: this.getFeatures, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'getFeatures', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featuresSchema') }, }), ], }); this.route({ method: 'post', path: PATH, handler: this.createFeature, permission: permissions_1.CREATE_FEATURE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'createFeature', requestBody: (0, create_request_schema_1.createRequestSchema)('createFeatureSchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'post', path: PATH_FEATURE_CLONE, acceptAnyContentType: true, handler: this.cloneFeature, permission: permissions_1.CREATE_FEATURE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'cloneFeature', requestBody: (0, create_request_schema_1.createRequestSchema)('cloneFeatureSchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'get', path: PATH_FEATURE, handler: this.getFeature, permission: permissions_1.NONE, middleware: [ openApiService.validPath({ operationId: 'getFeature', tags: ['Features'], description: 'This endpoint returns the information about the requested feature if the feature belongs to the specified project.', summary: 'Get a feature.', responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema'), 403: { description: 'You either do not have the required permissions or used an invalid URL.', }, ...(0, standard_responses_1.getStandardResponses)(401, 404), }, }), ], }); this.route({ method: 'put', path: PATH_FEATURE, handler: this.updateFeature, permission: permissions_1.UPDATE_FEATURE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'updateFeature', requestBody: (0, create_request_schema_1.createRequestSchema)('updateFeatureSchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'patch', path: PATH_FEATURE, handler: this.patchFeature, permission: permissions_1.UPDATE_FEATURE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'patchFeature', requestBody: (0, create_request_schema_1.createRequestSchema)('patchesSchema'), responses: { 200: (0, create_response_schema_1.createResponseSchema)('featureSchema') }, }), ], }); this.route({ method: 'delete', path: PATH_FEATURE, acceptAnyContentType: true, handler: this.archiveFeature, permission: permissions_1.DELETE_FEATURE, middleware: [ openApiService.validPath({ tags: ['Features'], operationId: 'archiveFeature', description: 'This endpoint archives the specified feature if the feature belongs to the specified project.', summary: 'Archive a feature.', responses: { 200: standard_responses_1.emptyResponse, 403: { description: 'You either do not have the required permissions or used an invalid URL.', }, ...(0, standard_responses_1.getStandardResponses)(401, 404), }, }), ], }); } async getFeatures(req, res) { const { projectId } = req.params; const features = await this.featureService.getFeatureOverview({ projectId, }); this.openApiService.respondWithValidation(200, res, features_schema_1.featuresSchema.$id, { version: 2, features: (0, serialize_dates_1.serializeDates)(features) }); } async cloneFeature(req, res) { const { projectId, featureName } = req.params; const { name, replaceGroupId } = req.body; const userName = (0, extract_user_1.extractUsername)(req); const created = await this.featureService.cloneFeatureToggle(featureName, projectId, name, replaceGroupId, userName); this.openApiService.respondWithValidation(201, res, feature_schema_1.featureSchema.$id, (0, serialize_dates_1.serializeDates)(created)); } async createFeature(req, res) { const { projectId } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const created = await this.featureService.createFeatureToggle(projectId, req.body, userName); this.openApiService.respondWithValidation(201, res, feature_schema_1.featureSchema.$id, (0, serialize_dates_1.serializeDates)(created)); } async getFeature(req, res) { const { featureName, projectId } = req.params; const { variantEnvironments } = req.query; const { user } = req; const feature = await this.featureService.getFeature({ featureName, archived: false, projectId, environmentVariants: variantEnvironments === 'true', userId: user.id, }); res.status(200).json(feature); } async updateFeature(req, res) { const { projectId, featureName } = req.params; const { createdAt, ...data } = req.body; const userName = (0, extract_user_1.extractUsername)(req); const created = await this.featureService.updateFeatureToggle(projectId, data, userName, featureName); this.openApiService.respondWithValidation(200, res, feature_schema_1.featureSchema.$id, (0, serialize_dates_1.serializeDates)(created)); } async patchFeature(req, res) { const { projectId, featureName } = req.params; const updated = await this.featureService.patchFeature(projectId, featureName, (0, extract_user_1.extractUsername)(req), req.body); this.openApiService.respondWithValidation(200, res, feature_schema_1.featureSchema.$id, (0, serialize_dates_1.serializeDates)(updated)); } async archiveFeature(req, res) { const { featureName, projectId } = req.params; const userName = (0, extract_user_1.extractUsername)(req); await this.featureService.archiveToggle(featureName, userName, projectId); res.status(202).send(); } async getFeatureEnvironment(req, res) { const { environment, featureName, projectId } = req.params; const environmentInfo = await this.featureService.getEnvironmentInfo(projectId, environment, featureName); this.openApiService.respondWithValidation(200, res, feature_schema_1.featureSchema.$id, (0, serialize_dates_1.serializeDates)(environmentInfo)); } async toggleFeatureEnvironmentOn(req, res) { const { featureName, environment, projectId } = req.params; await this.featureService.updateEnabled(projectId, featureName, environment, true, (0, extract_user_1.extractUsername)(req), req.user); res.status(200).end(); } async toggleFeatureEnvironmentOff(req, res) { const { featureName, environment, projectId } = req.params; await this.featureService.updateEnabled(projectId, featureName, environment, false, (0, extract_user_1.extractUsername)(req), req.user); res.status(200).end(); } async addFeatureStrategy(req, res) { const { projectId, featureName, environment } = req.params; const { ...strategyConfig } = req.body; if (!strategyConfig.segmentIds) { strategyConfig.segmentIds = []; } const userName = (0, extract_user_1.extractUsername)(req); const strategy = await this.featureService.createStrategy(strategyConfig, { environment, projectId, featureName }, userName, req.user); const updatedStrategy = await this.featureService.getStrategy(strategy.id); res.status(200).json(updatedStrategy); } async getFeatureStrategies(req, res) { const { projectId, featureName, environment } = req.params; const featureStrategies = await this.featureService.getStrategiesForEnvironment(projectId, featureName, environment); res.status(200).json(featureStrategies); } async setStrategiesSortOrder(req, res) { const { featureName } = req.params; await this.featureService.updateStrategiesSortOrder(featureName, req.body); res.status(200).send(); } async updateFeatureStrategy(req, res) { const { strategyId, environment, projectId, featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); if (!req.body.segmentIds) { req.body.segmentIds = []; } const updatedStrategy = await this.featureService.updateStrategy(strategyId, req.body, { environment, projectId, featureName }, userName, req.user); res.status(200).json(updatedStrategy); } async patchFeatureStrategy(req, res) { const { strategyId, projectId, environment, featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const patch = req.body; const strategy = await this.featureService.getStrategy(strategyId); const { newDocument } = (0, fast_json_patch_1.applyPatch)(strategy, patch); const updatedStrategy = await this.featureService.updateStrategy(strategyId, newDocument, { environment, projectId, featureName }, userName, req.user); res.status(200).json(updatedStrategy); } async getFeatureStrategy(req, res) { this.logger.info('Getting strategy'); const { strategyId } = req.params; this.logger.info(strategyId); const strategy = await this.featureService.getStrategy(strategyId); res.status(200).json(strategy); } async deleteFeatureStrategy(req, res) { this.logger.info('Deleting strategy'); const { environment, projectId, featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const { strategyId } = req.params; this.logger.info(strategyId); const strategy = await this.featureService.deleteStrategy(strategyId, { environment, projectId, featureName }, userName, req.user); res.status(200).json(strategy); } async updateStrategyParameter(req, res) { const { strategyId, environment, projectId, featureName } = req.params; const userName = (0, extract_user_1.extractUsername)(req); const { name, value } = req.body; const updatedStrategy = await this.featureService.updateStrategyParameter(strategyId, name, value, { environment, projectId, featureName }, userName); res.status(200).json(updatedStrategy); } async getStrategyParameters(req, res) { this.logger.info('Getting strategy parameters'); const { strategyId } = req.params; const strategy = await this.featureService.getStrategy(strategyId); res.status(200).json(strategy.parameters); } } exports.default = ProjectFeaturesController; //# sourceMappingURL=features.js.map