unleash-server
Version:
Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.
430 lines • 20.1 kB
JavaScript
"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