mirror of
https://github.com/Unleash/unleash.git
synced 2024-10-18 20:09:08 +02:00
8bf4214ddb
* feat: Handle database connection errors with 500 - If database goes away while unleash is running, unleash now stays running, but all api endpoints will return 500. - This includes our health endpoint, which allows k8s or similar orchestrators to decide what should be done, rather than Unleash terminating unexpectedly
210 lines
5.9 KiB
JavaScript
210 lines
5.9 KiB
JavaScript
const Controller = require('../controller');
|
|
|
|
const { handleErrors } = require('./util');
|
|
const extractUser = require('../../extract-user');
|
|
const {
|
|
UPDATE_FEATURE,
|
|
DELETE_FEATURE,
|
|
CREATE_FEATURE,
|
|
} = require('../../permissions');
|
|
|
|
const version = 1;
|
|
const fields = [
|
|
'name',
|
|
'description',
|
|
'type',
|
|
'project',
|
|
'enabled',
|
|
'stale',
|
|
'strategies',
|
|
'variants',
|
|
'createdAt',
|
|
'lastSeenAt',
|
|
];
|
|
|
|
class FeatureController extends Controller {
|
|
constructor(config, { featureToggleService }) {
|
|
super(config);
|
|
this.featureService = featureToggleService;
|
|
this.logger = config.getLogger('/admin-api/feature.js');
|
|
|
|
this.get('/', this.getAllToggles);
|
|
this.post('/', this.createToggle, CREATE_FEATURE);
|
|
this.get('/:featureName', this.getToggle);
|
|
this.put('/:featureName', this.updateToggle, UPDATE_FEATURE);
|
|
this.delete('/:featureName', this.deleteToggle, DELETE_FEATURE);
|
|
this.post('/validate', this.validate);
|
|
this.post('/:featureName/toggle', this.toggle, UPDATE_FEATURE);
|
|
this.post('/:featureName/toggle/on', this.toggleOn, UPDATE_FEATURE);
|
|
this.post('/:featureName/toggle/off', this.toggleOff, UPDATE_FEATURE);
|
|
this.post('/:featureName/stale/on', this.staleOn, UPDATE_FEATURE);
|
|
this.post('/:featureName/stale/off', this.staleOff, UPDATE_FEATURE);
|
|
this.get('/:featureName/tags', this.listTags, UPDATE_FEATURE);
|
|
this.post('/:featureName/tags', this.addTag, UPDATE_FEATURE);
|
|
this.delete(
|
|
'/:featureName/tags/:type/:value',
|
|
this.removeTag,
|
|
UPDATE_FEATURE,
|
|
);
|
|
}
|
|
|
|
async getAllToggles(req, res) {
|
|
try {
|
|
const features = await this.featureService.getFeatures(
|
|
req.query,
|
|
fields,
|
|
);
|
|
res.json({ version, features });
|
|
} catch (err) {
|
|
handleErrors(res, this.logger, err);
|
|
}
|
|
}
|
|
|
|
async getToggle(req, res) {
|
|
try {
|
|
const name = req.params.featureName;
|
|
const feature = await this.featureService.getFeature(name);
|
|
res.json(feature).end();
|
|
} catch (err) {
|
|
res.status(404).json({ error: 'Could not find feature' });
|
|
}
|
|
}
|
|
|
|
async listTags(req, res) {
|
|
try {
|
|
const tags = await this.featureService.listTags(
|
|
req.params.featureName,
|
|
);
|
|
res.json({ version, tags });
|
|
} catch (err) {
|
|
handleErrors(res, this.logger, err);
|
|
}
|
|
}
|
|
|
|
async addTag(req, res) {
|
|
const { featureName } = req.params;
|
|
const userName = extractUser(req);
|
|
try {
|
|
const tag = await this.featureService.addTag(
|
|
featureName,
|
|
req.body,
|
|
userName,
|
|
);
|
|
res.status(201).json(tag);
|
|
} catch (err) {
|
|
handleErrors(res, this.logger, err);
|
|
}
|
|
}
|
|
|
|
async removeTag(req, res) {
|
|
const { featureName, type, value } = req.params;
|
|
const userName = extractUser(req);
|
|
try {
|
|
await this.featureService.removeTag(
|
|
featureName,
|
|
{ type, value },
|
|
userName,
|
|
);
|
|
res.status(200).end();
|
|
} catch (err) {
|
|
handleErrors(res, this.logger, err);
|
|
}
|
|
}
|
|
|
|
async validate(req, res) {
|
|
const { name } = req.body;
|
|
|
|
try {
|
|
await this.featureService.validateName({ name });
|
|
res.status(200).end();
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
|
|
async createToggle(req, res) {
|
|
const userName = extractUser(req);
|
|
|
|
try {
|
|
await this.featureService.createFeatureToggle(req.body, userName);
|
|
res.status(201).end();
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
|
|
async updateToggle(req, res) {
|
|
const { featureName } = req.params;
|
|
const userName = extractUser(req);
|
|
const updatedFeature = req.body;
|
|
|
|
updatedFeature.name = featureName;
|
|
|
|
try {
|
|
await this.featureService.updateToggle(updatedFeature, userName);
|
|
res.status(200).end();
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
|
|
// Kept to keep backward compatibility
|
|
async toggle(req, res) {
|
|
const userName = extractUser(req);
|
|
try {
|
|
const name = req.params.featureName;
|
|
const feature = await this.featureService.toggle(name, userName);
|
|
res.status(200).json(feature);
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
|
|
async toggleOn(req, res) {
|
|
await this._updateField('enabled', true, req, res);
|
|
}
|
|
|
|
async toggleOff(req, res) {
|
|
await this._updateField('enabled', false, req, res);
|
|
}
|
|
|
|
async staleOn(req, res) {
|
|
await this._updateField('stale', true, req, res);
|
|
}
|
|
|
|
async staleOff(req, res) {
|
|
await this._updateField('stale', false, req, res);
|
|
}
|
|
|
|
async _updateField(field, value, req, res) {
|
|
const { featureName } = req.params;
|
|
const userName = extractUser(req);
|
|
|
|
try {
|
|
const feature = await this.featureService.updateField(
|
|
featureName,
|
|
field,
|
|
value,
|
|
userName,
|
|
);
|
|
res.json(feature).end();
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
|
|
async deleteToggle(req, res) {
|
|
const { featureName } = req.params;
|
|
const userName = extractUser(req);
|
|
|
|
try {
|
|
await this.featureService.archiveToggle(featureName, userName);
|
|
res.status(200).end();
|
|
} catch (error) {
|
|
handleErrors(res, this.logger, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = FeatureController;
|