mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: Controller wraps handler with try/catch (#909)
By having the controller perform try/catch around the handler function allows us to add extra safety to all our controllers and safeguards that we will always catch exceptions thrown by a controller method.
This commit is contained in:
		
							parent
							
								
									0faa0cd075
								
							
						
					
					
						commit
						2bcdb5ec31
					
				| @ -46,3 +46,15 @@ When you're done making changes and you'd like to propose them for review by ope | |||||||
| Congratulations! The whole Unleash community thanks you. :sparkles: | Congratulations! The whole Unleash community thanks you. :sparkles: | ||||||
| 
 | 
 | ||||||
| Once your PR is merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/unleash/Unleash/graphs/contributors). | Once your PR is merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/unleash/Unleash/graphs/contributors). | ||||||
|  | 
 | ||||||
|  | ## Nice to know | ||||||
|  | 
 | ||||||
|  | ### Controllers | ||||||
|  | 
 | ||||||
|  | In order to handle HTTP requests we have an abstraction called [Controller](https://github.com/Unleash/unleash/blob/master/src/lib/routes/controller.ts). If you want to introduce a new route handler for a specific path (and sub pats) you should implement a controller class which extends the base Controller. An example to follow is the [routes/admin-api/feature.ts](https://github.com/Unleash/unleash/blob/master/src/lib/routes/admin-api/feature.ts) implementation.  | ||||||
|  | 
 | ||||||
|  | The controller takes care of the following: | ||||||
|  | - try/catch RequestHandler method | ||||||
|  | - error handling with proper response code if they fail | ||||||
|  | - `await` the RequestHandler method if it returns a promise (so you don't have to) | ||||||
|  | - access control so that you can just list the required permission for a RequestHandler and the base Controller will make sure the user have these permissions.  | ||||||
| @ -1,5 +1,5 @@ | |||||||
| import joi from 'joi'; | import joi from 'joi'; | ||||||
| import { nameType } from '../routes/admin-api/util'; | import { nameType } from '../routes/util'; | ||||||
| import { tagTypeSchema } from '../services/tag-type-schema'; | import { tagTypeSchema } from '../services/tag-type-schema'; | ||||||
| 
 | 
 | ||||||
| export const addonDefinitionSchema = joi.object().keys({ | export const addonDefinitionSchema = joi.object().keys({ | ||||||
|  | |||||||
| @ -1,14 +1,14 @@ | |||||||
|  | import { IAuthRequest } from 'lib/routes/unleash-types'; | ||||||
| import supertest from 'supertest'; | import supertest from 'supertest'; | ||||||
| import express from 'express'; | import express from 'express'; | ||||||
| import noAuthentication from './no-authentication'; | import noAuthentication from './no-authentication'; | ||||||
| import { IUserRequest } from '../routes/admin-api/user'; |  | ||||||
| 
 | 
 | ||||||
| test('should add dummy user object to all requests', () => { | test('should add dummy user object to all requests', () => { | ||||||
|     expect.assertions(1); |     expect.assertions(1); | ||||||
| 
 | 
 | ||||||
|     const app = express(); |     const app = express(); | ||||||
|     noAuthentication('', app); |     noAuthentication('', app); | ||||||
|     app.get('/api/admin/test', (req: IUserRequest<any, any, any, any>, res) => { |     app.get('/api/admin/test', (req: IAuthRequest<any, any, any, any>, res) => { | ||||||
|         const user = { ...req.user }; |         const user = { ...req.user }; | ||||||
| 
 | 
 | ||||||
|         return res.status(200).json(user).end(); |         return res.status(200).json(user).end(); | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ import { Logger } from '../../logger'; | |||||||
| import AddonService from '../../services/addon-service'; | import AddonService from '../../services/addon-service'; | ||||||
| 
 | 
 | ||||||
| import extractUser from '../../extract-user'; | import extractUser from '../../extract-user'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import { | import { | ||||||
|     CREATE_ADDON, |     CREATE_ADDON, | ||||||
|     UPDATE_ADDON, |     UPDATE_ADDON, | ||||||
| @ -34,13 +33,9 @@ class AddonController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getAddons(req: Request, res: Response): Promise<void> { |     async getAddons(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const addons = await this.addonService.getAddons(); |         const addons = await this.addonService.getAddons(); | ||||||
|         const providers = this.addonService.getProviderDefinitions(); |         const providers = this.addonService.getProviderDefinitions(); | ||||||
|         res.json({ addons, providers }); |         res.json({ addons, providers }); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getAddon( |     async getAddon( | ||||||
| @ -48,12 +43,8 @@ class AddonController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { id } = req.params; |         const { id } = req.params; | ||||||
|         try { |  | ||||||
|         const addon = await this.addonService.getAddon(id); |         const addon = await this.addonService.getAddon(id); | ||||||
|         res.json(addon); |         res.json(addon); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateAddon( |     async updateAddon( | ||||||
| @ -64,27 +55,15 @@ class AddonController extends Controller { | |||||||
|         const createdBy = extractUser(req); |         const createdBy = extractUser(req); | ||||||
|         const data = req.body; |         const data = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |         const addon = await this.addonService.updateAddon(id, data, createdBy); | ||||||
|             const addon = await this.addonService.updateAddon( |  | ||||||
|                 id, |  | ||||||
|                 data, |  | ||||||
|                 createdBy, |  | ||||||
|             ); |  | ||||||
|         res.status(200).json(addon); |         res.status(200).json(addon); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createAddon(req: Request, res: Response): Promise<void> { |     async createAddon(req: Request, res: Response): Promise<void> { | ||||||
|         const createdBy = extractUser(req); |         const createdBy = extractUser(req); | ||||||
|         const data = req.body; |         const data = req.body; | ||||||
|         try { |  | ||||||
|         const addon = await this.addonService.createAddon(data, createdBy); |         const addon = await this.addonService.createAddon(data, createdBy); | ||||||
|         res.status(201).json(addon); |         res.status(201).json(addon); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async deleteAddon( |     async deleteAddon( | ||||||
| @ -93,12 +72,8 @@ class AddonController extends Controller { | |||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { id } = req.params; |         const { id } = req.params; | ||||||
|         const username = extractUser(req); |         const username = extractUser(req); | ||||||
|         try { |  | ||||||
|         await this.addonService.removeAddon(id, username); |         await this.addonService.removeAddon(id, username); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| export default AddonController; | export default AddonController; | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| import { Request, Response } from 'express'; | import { Request, Response } from 'express'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| @ -36,13 +35,10 @@ export default class ArchiveController extends Controller { | |||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async getArchivedFeatures(req, res): Promise<void> { |     async getArchivedFeatures(req, res): Promise<void> { | ||||||
|         try { |         const features = await this.featureService.getMetadataForAllFeatures( | ||||||
|             const features = |             true, | ||||||
|                 await this.featureService.getMetadataForAllFeatures(true); |         ); | ||||||
|         res.json({ version: 2, features }); |         res.json({ version: 2, features }); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async deleteFeature( |     async deleteFeature( | ||||||
| @ -51,25 +47,16 @@ export default class ArchiveController extends Controller { | |||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const user = extractUser(req); |         const user = extractUser(req); | ||||||
|         try { |  | ||||||
|         await this.featureService.deleteFeature(featureName, user); |         await this.featureService.deleteFeature(featureName, user); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async reviveFeatureToggle(req, res): Promise<void> { |     async reviveFeatureToggle(req, res): Promise<void> { | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|         await this.featureService.reviveToggle(featureName, userName); |         await this.featureService.reviveToggle(featureName, userName); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ import { Request, Response } from 'express'; | |||||||
| 
 | 
 | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| 
 | 
 | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import extractUser from '../../extract-user'; | import extractUser from '../../extract-user'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
| @ -45,12 +44,8 @@ class ContextController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getContextFields(req: Request, res: Response): Promise<void> { |     async getContextFields(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const fields = await this.contextService.getAll(); |         const fields = await this.contextService.getAll(); | ||||||
|         res.status(200).json(fields).end(); |         res.status(200).json(fields).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getContextField(req: Request, res: Response): Promise<void> { |     async getContextField(req: Request, res: Response): Promise<void> { | ||||||
| @ -69,12 +64,8 @@ class ContextController extends Controller { | |||||||
|         const value = req.body; |         const value = req.body; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.contextService.createContextField(value, userName); |         await this.contextService.createContextField(value, userName); | ||||||
|         res.status(201).end(); |         res.status(201).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateContextField(req: Request, res: Response): Promise<void> { |     async updateContextField(req: Request, res: Response): Promise<void> { | ||||||
| @ -84,38 +75,23 @@ class ContextController extends Controller { | |||||||
| 
 | 
 | ||||||
|         contextField.name = name; |         contextField.name = name; | ||||||
| 
 | 
 | ||||||
|         try { |         await this.contextService.updateContextField(contextField, userName); | ||||||
|             await this.contextService.updateContextField( |  | ||||||
|                 contextField, |  | ||||||
|                 userName, |  | ||||||
|             ); |  | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async deleteContextField(req: Request, res: Response): Promise<void> { |     async deleteContextField(req: Request, res: Response): Promise<void> { | ||||||
|         const name = req.params.contextField; |         const name = req.params.contextField; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.contextService.deleteContextField(name, userName); |         await this.contextService.deleteContextField(name, userName); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async validate(req: Request, res: Response): Promise<void> { |     async validate(req: Request, res: Response): Promise<void> { | ||||||
|         const { name } = req.body; |         const { name } = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.contextService.validateName(name); |         await this.contextService.validateName(name); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| export default ContextController; | export default ContextController; | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| import { ADMIN } from '../../types/permissions'; | import { ADMIN } from '../../types/permissions'; | ||||||
| import { TemplateFormat } from '../../services/email-service'; | import { TemplateFormat } from '../../services/email-service'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| 
 | 
 | ||||||
| @ -20,7 +19,6 @@ export default class EmailController extends Controller { | |||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async getHtmlPreview(req, res): Promise<void> { |     async getHtmlPreview(req, res): Promise<void> { | ||||||
|         try { |  | ||||||
|         const { template } = req.params; |         const { template } = req.params; | ||||||
|         const ctx = req.query; |         const ctx = req.query; | ||||||
|         const data = await this.emailService.compileTemplate( |         const data = await this.emailService.compileTemplate( | ||||||
| @ -32,14 +30,10 @@ export default class EmailController extends Controller { | |||||||
|         res.status(200); |         res.status(200); | ||||||
|         res.send(data); |         res.send(data); | ||||||
|         res.end(); |         res.end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async getTextPreview(req, res) { |     async getTextPreview(req, res) { | ||||||
|         try { |  | ||||||
|         const { template } = req.params; |         const { template } = req.params; | ||||||
|         const ctx = req.query; |         const ctx = req.query; | ||||||
|         const data = await this.emailService.compileTemplate( |         const data = await this.emailService.compileTemplate( | ||||||
| @ -51,9 +45,6 @@ export default class EmailController extends Controller { | |||||||
|         res.status(200); |         res.status(200); | ||||||
|         res.send(data); |         res.send(data); | ||||||
|         res.end(); |         res.end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| module.exports = EmailController; | module.exports = EmailController; | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import { IUnleashConfig } from '../../types/option'; | |||||||
| import { IEnvironment } from '../../types/model'; | import { IEnvironment } from '../../types/model'; | ||||||
| import EnvironmentService from '../../services/environment-service'; | import EnvironmentService from '../../services/environment-service'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| import { handleErrors } from './util'; | import { handleErrors } from '../util'; | ||||||
| import { ADMIN } from '../../types/permissions'; | import { ADMIN } from '../../types/permissions'; | ||||||
| 
 | 
 | ||||||
| interface EnvironmentParam { | interface EnvironmentParam { | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| import { handleErrors } from './util'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import EventService from '../../services/event-service'; | import EventService from '../../services/event-service'; | ||||||
| @ -25,22 +24,15 @@ export default class EventController extends Controller { | |||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async getEvents(req, res): Promise<void> { |     async getEvents(req, res): Promise<void> { | ||||||
|         try { |  | ||||||
|         const events = await this.eventService.getEvents(); |         const events = await this.eventService.getEvents(); | ||||||
|         eventDiffer.addDiffs(events); |         eventDiffer.addDiffs(events); | ||||||
|         res.json({ version, events }); |         res.json({ version, events }); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     async getEventsForToggle(req, res): Promise<void> { |     async getEventsForToggle(req, res): Promise<void> { | ||||||
|         const toggleName = req.params.name; |         const toggleName = req.params.name; | ||||||
|         try { |         const events = await this.eventService.getEventsForToggle(toggleName); | ||||||
|             const events = await this.eventService.getEventsForToggle( |  | ||||||
|                 toggleName, |  | ||||||
|             ); |  | ||||||
| 
 | 
 | ||||||
|         if (events) { |         if (events) { | ||||||
|             eventDiffer.addDiffs(events); |             eventDiffer.addDiffs(events); | ||||||
| @ -48,9 +40,6 @@ export default class EventController extends Controller { | |||||||
|         } else { |         } else { | ||||||
|             res.status(404).json({ error: 'Could not find events' }); |             res.status(404).json({ error: 'Could not find events' }); | ||||||
|         } |         } | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| import { Request, Response } from 'express'; | import { Request, Response } from 'express'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import FeatureTypeService from '../../services/feature-type-service'; | import FeatureTypeService from '../../services/feature-type-service'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| @ -26,12 +25,8 @@ export default class FeatureTypeController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getAllFeatureTypes(req: Request, res: Response): Promise<void> { |     async getAllFeatureTypes(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const types = await this.featureTypeService.getAll(); |         const types = await this.featureTypeService.getAll(); | ||||||
|         res.json({ version, types }); |         res.json({ version, types }); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ import { Request, Response } from 'express'; | |||||||
| 
 | 
 | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| 
 | 
 | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import extractUser from '../../extract-user'; | import extractUser from '../../extract-user'; | ||||||
| import { | import { | ||||||
|     UPDATE_FEATURE, |     UPDATE_FEATURE, | ||||||
| @ -94,22 +93,15 @@ class FeatureController extends Controller { | |||||||
| 
 | 
 | ||||||
|     async getAllToggles(req: Request, res: Response): Promise<void> { |     async getAllToggles(req: Request, res: Response): Promise<void> { | ||||||
|         const query = await this.prepQuery(req.query); |         const query = await this.prepQuery(req.query); | ||||||
|         try { |         const features = await this.featureService2.getFeatureToggles(query); | ||||||
|             const features = await this.featureService2.getFeatureToggles( |  | ||||||
|                 query, |  | ||||||
|             ); |  | ||||||
| 
 | 
 | ||||||
|         res.json({ version, features }); |         res.json({ version, features }); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getToggle( |     async getToggle( | ||||||
|         req: Request<{ featureName: string }, any, any, any>, |         req: Request<{ featureName: string }, any, any, any>, | ||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         try { |  | ||||||
|         const name = req.params.featureName; |         const name = req.params.featureName; | ||||||
|         const feature = await this.featureService2.getFeatureToggle(name); |         const feature = await this.featureService2.getFeatureToggle(name); | ||||||
|         const strategies = |         const strategies = | ||||||
| @ -119,75 +111,52 @@ class FeatureController extends Controller { | |||||||
|             ...feature, |             ...feature, | ||||||
|             strategies, |             strategies, | ||||||
|         }).end(); |         }).end(); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO
 |  | ||||||
|     async listTags(req: Request, res: Response): Promise<void> { |     async listTags(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const tags = await this.featureTagService.listTags( |         const tags = await this.featureTagService.listTags( | ||||||
|             req.params.featureName, |             req.params.featureName, | ||||||
|         ); |         ); | ||||||
|         res.json({ version, tags }); |         res.json({ version, tags }); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO
 |  | ||||||
|     async addTag(req: Request, res: Response): Promise<void> { |     async addTag(req: Request, res: Response): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         try { |  | ||||||
|         const tag = await this.featureTagService.addTag( |         const tag = await this.featureTagService.addTag( | ||||||
|             featureName, |             featureName, | ||||||
|             req.body, |             req.body, | ||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.status(201).json(tag); |         res.status(201).json(tag); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO
 |     // TODO
 | ||||||
|     async removeTag(req: Request, res: Response): Promise<void> { |     async removeTag(req: Request, res: Response): Promise<void> { | ||||||
|         const { featureName, type, value } = req.params; |         const { featureName, type, value } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         try { |  | ||||||
|         await this.featureTagService.removeTag( |         await this.featureTagService.removeTag( | ||||||
|             featureName, |             featureName, | ||||||
|             { type, value }, |             { type, value }, | ||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async validate(req: Request, res: Response): Promise<void> { |     async validate(req: Request, res: Response): Promise<void> { | ||||||
|         const { name } = req.body; |         const { name } = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.featureService2.validateName(name); |         await this.featureService2.validateName(name); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createToggle(req: Request, res: Response): Promise<void> { |     async createToggle(req: Request, res: Response): Promise<void> { | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         const toggle = req.body; |         const toggle = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         const validatedToggle = await featureSchema.validateAsync(toggle); |         const validatedToggle = await featureSchema.validateAsync(toggle); | ||||||
|         const { enabled } = validatedToggle; |         const { enabled } = validatedToggle; | ||||||
|             const createdFeature = |         const createdFeature = await this.featureService2.createFeatureToggle( | ||||||
|                 await this.featureService2.createFeatureToggle( |  | ||||||
|             validatedToggle.project, |             validatedToggle.project, | ||||||
|             validatedToggle, |             validatedToggle, | ||||||
|             userName, |             userName, | ||||||
| @ -213,10 +182,6 @@ class FeatureController extends Controller { | |||||||
|             enabled, |             enabled, | ||||||
|             strategies, |             strategies, | ||||||
|         }); |         }); | ||||||
|         } catch (error) { |  | ||||||
|             this.logger.warn(error); |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateToggle(req: Request, res: Response): Promise<void> { |     async updateToggle(req: Request, res: Response): Promise<void> { | ||||||
| @ -230,7 +195,6 @@ class FeatureController extends Controller { | |||||||
|             featureName, |             featureName, | ||||||
|         ); |         ); | ||||||
|         if (featureToggleExists) { |         if (featureToggleExists) { | ||||||
|             try { |  | ||||||
|             await this.featureService2.getFeature(featureName); |             await this.featureService2.getFeature(featureName); | ||||||
|             const projectId = await this.featureService2.getProjectId( |             const projectId = await this.featureService2.getProjectId( | ||||||
|                 updatedFeature.name, |                 updatedFeature.name, | ||||||
| @ -243,9 +207,7 @@ class FeatureController extends Controller { | |||||||
|                 userName, |                 userName, | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|                 await this.featureService2.removeAllStrategiesForEnv( |             await this.featureService2.removeAllStrategiesForEnv(featureName); | ||||||
|                     featureName, |  | ||||||
|                 ); |  | ||||||
|             let strategies; |             let strategies; | ||||||
|             if (updatedFeature.strategies) { |             if (updatedFeature.strategies) { | ||||||
|                 strategies = await Promise.all( |                 strategies = await Promise.all( | ||||||
| @ -269,9 +231,6 @@ class FeatureController extends Controller { | |||||||
|                 enabled, |                 enabled, | ||||||
|                 strategies: strategies || [], |                 strategies: strategies || [], | ||||||
|             }); |             }); | ||||||
|             } catch (error) { |  | ||||||
|                 handleErrors(res, this.logger, error); |  | ||||||
|             } |  | ||||||
|         } else { |         } else { | ||||||
|             res.status(404) |             res.status(404) | ||||||
|                 .json({ |                 .json({ | ||||||
| @ -285,7 +244,6 @@ class FeatureController extends Controller { | |||||||
|     // Kept to keep backward compatibility
 |     // Kept to keep backward compatibility
 | ||||||
|     async toggle(req: Request, res: Response): Promise<void> { |     async toggle(req: Request, res: Response): Promise<void> { | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         try { |  | ||||||
|         const name = req.params.featureName; |         const name = req.params.featureName; | ||||||
|         const feature = await this.featureService2.toggle( |         const feature = await this.featureService2.toggle( | ||||||
|             name, |             name, | ||||||
| @ -293,15 +251,11 @@ class FeatureController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.status(200).json(feature); |         res.status(200).json(feature); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async toggleOn(req: Request, res: Response): Promise<void> { |     async toggleOn(req: Request, res: Response): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         try { |  | ||||||
|         const feature = await this.featureService2.updateEnabled( |         const feature = await this.featureService2.updateEnabled( | ||||||
|             featureName, |             featureName, | ||||||
|             GLOBAL_ENV, |             GLOBAL_ENV, | ||||||
| @ -309,15 +263,11 @@ class FeatureController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.json(feature); |         res.json(feature); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async toggleOff(req: Request, res: Response): Promise<void> { |     async toggleOff(req: Request, res: Response): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         try { |  | ||||||
|         const feature = await this.featureService2.updateEnabled( |         const feature = await this.featureService2.updateEnabled( | ||||||
|             featureName, |             featureName, | ||||||
|             GLOBAL_ENV, |             GLOBAL_ENV, | ||||||
| @ -325,13 +275,9 @@ class FeatureController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.json(feature); |         res.json(feature); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async staleOn(req: Request, res: Response): Promise<void> { |     async staleOn(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         const feature = await this.featureService2.updateStale( |         const feature = await this.featureService2.updateStale( | ||||||
| @ -340,13 +286,9 @@ class FeatureController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.json(feature).end(); |         res.json(feature).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async staleOff(req: Request, res: Response): Promise<void> { |     async staleOff(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         const feature = await this.featureService2.updateStale( |         const feature = await this.featureService2.updateStale( | ||||||
| @ -355,21 +297,14 @@ class FeatureController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.json(feature).end(); |         res.json(feature).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async archiveToggle(req: Request, res: Response): Promise<void> { |     async archiveToggle(req: Request, res: Response): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.featureService2.archiveToggle(featureName, userName); |         await this.featureService2.archiveToggle(featureName, userName); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| export default FeatureController; | export default FeatureController; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { Request, Response } from 'express'; | import { Request, Response } from 'express'; | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import { handleErrors } from './util'; | import { handleErrors } from '../util'; | ||||||
| import { UPDATE_APPLICATION } from '../../types/permissions'; | import { UPDATE_APPLICATION } from '../../types/permissions'; | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { IUnleashConfig } from '../../../types/option'; | |||||||
| import { IUnleashServices } from '../../../types/services'; | import { IUnleashServices } from '../../../types/services'; | ||||||
| import { Logger } from '../../../logger'; | import { Logger } from '../../../logger'; | ||||||
| import EnvironmentService from '../../../services/environment-service'; | import EnvironmentService from '../../../services/environment-service'; | ||||||
| import { handleErrors } from '../util'; | import { handleErrors } from '../../util'; | ||||||
| import { UPDATE_PROJECT } from '../../../types/permissions'; | import { UPDATE_PROJECT } from '../../../types/permissions'; | ||||||
| 
 | 
 | ||||||
| const PREFIX = '/:projectId/environments'; | const PREFIX = '/:projectId/environments'; | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ import { | |||||||
|     IConstraint, |     IConstraint, | ||||||
|     IStrategyConfig, |     IStrategyConfig, | ||||||
| } from '../../../types/model'; | } from '../../../types/model'; | ||||||
| import { handleErrors } from '../util'; |  | ||||||
| import extractUsername from '../../../extract-user'; | import extractUsername from '../../../extract-user'; | ||||||
| import ProjectHealthService from '../../../services/project-health-service'; | import ProjectHealthService from '../../../services/project-health-service'; | ||||||
| 
 | 
 | ||||||
| @ -111,14 +110,10 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { projectId } = req.params; |         const { projectId } = req.params; | ||||||
|         try { |  | ||||||
|         const features = await this.featureService.getFeatureToggles({ |         const features = await this.featureService.getFeatureToggles({ | ||||||
|             project: [projectId], |             project: [projectId], | ||||||
|         }); |         }); | ||||||
|         res.json({ version: 1, features }); |         res.json({ version: 1, features }); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createFeatureToggle( |     async createFeatureToggle( | ||||||
| @ -126,7 +121,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { projectId } = req.params; |         const { projectId } = req.params; | ||||||
|         try { |  | ||||||
|         const userName = extractUsername(req); |         const userName = extractUsername(req); | ||||||
|         const created = await this.featureService.createFeatureToggle( |         const created = await this.featureService.createFeatureToggle( | ||||||
|             projectId, |             projectId, | ||||||
| @ -134,9 +128,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|             userName, |             userName, | ||||||
|         ); |         ); | ||||||
|         res.status(201).json(created); |         res.status(201).json(created); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getEnvironment( |     async getEnvironment( | ||||||
| @ -144,17 +135,12 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { environment, featureName, projectId } = req.params; |         const { environment, featureName, projectId } = req.params; | ||||||
|         try { |         const environmentInfo = await this.featureService.getEnvironmentInfo( | ||||||
|             const environmentInfo = |  | ||||||
|                 await this.featureService.getEnvironmentInfo( |  | ||||||
|             projectId, |             projectId, | ||||||
|             environment, |             environment, | ||||||
|             featureName, |             featureName, | ||||||
|         ); |         ); | ||||||
|         res.status(200).json(environmentInfo); |         res.status(200).json(environmentInfo); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getFeature( |     async getFeature( | ||||||
| @ -162,12 +148,8 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { featureName } = req.params; |         const { featureName } = req.params; | ||||||
|         try { |  | ||||||
|         const feature = await this.featureService.getFeature(featureName); |         const feature = await this.featureService.getFeature(featureName); | ||||||
|         res.status(200).json(feature); |         res.status(200).json(feature); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async toggleEnvironmentOn( |     async toggleEnvironmentOn( | ||||||
| @ -175,7 +157,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { featureName, environment } = req.params; |         const { featureName, environment } = req.params; | ||||||
|         try { |  | ||||||
|         await this.featureService.updateEnabled( |         await this.featureService.updateEnabled( | ||||||
|             featureName, |             featureName, | ||||||
|             environment, |             environment, | ||||||
| @ -183,9 +164,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|             extractUsername(req), |             extractUsername(req), | ||||||
|         ); |         ); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async toggleEnvironmentOff( |     async toggleEnvironmentOff( | ||||||
| @ -193,7 +171,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { featureName, environment } = req.params; |         const { featureName, environment } = req.params; | ||||||
|         try { |  | ||||||
|         await this.featureService.updateEnabled( |         await this.featureService.updateEnabled( | ||||||
|             featureName, |             featureName, | ||||||
|             environment, |             environment, | ||||||
| @ -201,9 +178,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|             extractUsername(req), |             extractUsername(req), | ||||||
|         ); |         ); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createFeatureStrategy( |     async createFeatureStrategy( | ||||||
| @ -211,7 +185,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { projectId, featureName, environment } = req.params; |         const { projectId, featureName, environment } = req.params; | ||||||
|         try { |  | ||||||
|         const featureStrategy = await this.featureService.createStrategy( |         const featureStrategy = await this.featureService.createStrategy( | ||||||
|             req.body, |             req.body, | ||||||
|             projectId, |             projectId, | ||||||
| @ -219,9 +192,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|             environment, |             environment, | ||||||
|         ); |         ); | ||||||
|         res.status(200).json(featureStrategy); |         res.status(200).json(featureStrategy); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getFeatureStrategies( |     async getFeatureStrategies( | ||||||
| @ -229,7 +199,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { projectId, featureName, environment } = req.params; |         const { projectId, featureName, environment } = req.params; | ||||||
|         try { |  | ||||||
|         const featureStrategies = |         const featureStrategies = | ||||||
|             await this.featureService.getStrategiesForEnvironment( |             await this.featureService.getStrategiesForEnvironment( | ||||||
|                 projectId, |                 projectId, | ||||||
| @ -237,9 +206,6 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|                 environment, |                 environment, | ||||||
|             ); |             ); | ||||||
|         res.status(200).json(featureStrategies); |         res.status(200).json(featureStrategies); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateStrategy( |     async updateStrategy( | ||||||
| @ -247,15 +213,11 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { strategyId } = req.params; |         const { strategyId } = req.params; | ||||||
|         try { |  | ||||||
|         const updatedStrategy = await this.featureService.updateStrategy( |         const updatedStrategy = await this.featureService.updateStrategy( | ||||||
|             strategyId, |             strategyId, | ||||||
|             req.body, |             req.body, | ||||||
|         ); |         ); | ||||||
|         res.status(200).json(updatedStrategy); |         res.status(200).json(updatedStrategy); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getStrategy( |     async getStrategy( | ||||||
| @ -265,11 +227,7 @@ export default class ProjectFeaturesController extends Controller { | |||||||
|         this.logger.info('Getting strategy'); |         this.logger.info('Getting strategy'); | ||||||
|         const { strategyId } = req.params; |         const { strategyId } = req.params; | ||||||
|         this.logger.info(strategyId); |         this.logger.info(strategyId); | ||||||
|         try { |  | ||||||
|         const strategy = await this.featureService.getStrategy(strategyId); |         const strategy = await this.featureService.getStrategy(strategyId); | ||||||
|         res.status(200).json(strategy); |         res.status(200).json(strategy); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import { IUnleashConfig } from '../../../types/option'; | |||||||
| import ProjectHealthService from '../../../services/project-health-service'; | import ProjectHealthService from '../../../services/project-health-service'; | ||||||
| import { Logger } from '../../../logger'; | import { Logger } from '../../../logger'; | ||||||
| import { IArchivedQuery, IProjectParam } from '../../../types/model'; | import { IArchivedQuery, IProjectParam } from '../../../types/model'; | ||||||
| import { handleErrors } from '../util'; | import { handleErrors } from '../../util'; | ||||||
| 
 | 
 | ||||||
| export default class ProjectHealthReport extends Controller { | export default class ProjectHealthReport extends Controller { | ||||||
|     private projectHealthService: ProjectHealthService; |     private projectHealthService: ProjectHealthService; | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ import { Request, Response } from 'express'; | |||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import { ADMIN } from '../../types/permissions'; | import { ADMIN } from '../../types/permissions'; | ||||||
| import extractUser from '../../extract-user'; | import extractUser from '../../extract-user'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| @ -43,7 +42,6 @@ class StateController extends Controller { | |||||||
|         const userName = extractUser(req); |         const userName = extractUser(req); | ||||||
|         const { drop, keep } = req.query; |         const { drop, keep } = req.query; | ||||||
|         // TODO: Should override request type so file is a type on request
 |         // TODO: Should override request type so file is a type on request
 | ||||||
|         try { |  | ||||||
|         let data; |         let data; | ||||||
|         // @ts-ignore
 |         // @ts-ignore
 | ||||||
|         if (req.file) { |         if (req.file) { | ||||||
| @ -66,9 +64,6 @@ class StateController extends Controller { | |||||||
|             keepExisting: paramToBool(keep, true), |             keepExisting: paramToBool(keep, true), | ||||||
|         }); |         }); | ||||||
|         res.sendStatus(202); |         res.sendStatus(202); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async export(req: Request, res: Response): Promise<void> { |     async export(req: Request, res: Response): Promise<void> { | ||||||
| @ -84,7 +79,6 @@ class StateController extends Controller { | |||||||
|         const includeTags = paramToBool(req.query.tags, true); |         const includeTags = paramToBool(req.query.tags, true); | ||||||
|         const includeEnvironments = paramToBool(req.query.environments, true); |         const includeEnvironments = paramToBool(req.query.environments, true); | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         const data = await this.stateService.export({ |         const data = await this.stateService.export({ | ||||||
|             includeStrategies, |             includeStrategies, | ||||||
|             includeFeatureToggles, |             includeFeatureToggles, | ||||||
| @ -97,18 +91,13 @@ class StateController extends Controller { | |||||||
|             if (downloadFile) { |             if (downloadFile) { | ||||||
|                 res.attachment(`export-${timestamp}.yml`); |                 res.attachment(`export-${timestamp}.yml`); | ||||||
|             } |             } | ||||||
|                 res.type('yaml').send( |             res.type('yaml').send(YAML.safeDump(data, { skipInvalid: true })); | ||||||
|                     YAML.safeDump(data, { skipInvalid: true }), |  | ||||||
|                 ); |  | ||||||
|         } else { |         } else { | ||||||
|             if (downloadFile) { |             if (downloadFile) { | ||||||
|                 res.attachment(`export-${timestamp}.json`); |                 res.attachment(`export-${timestamp}.json`); | ||||||
|             } |             } | ||||||
|             res.json(data); |             res.json(data); | ||||||
|         } |         } | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| export default StateController; | export default StateController; | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import { Logger } from '../../logger'; | |||||||
| const Controller = require('../controller'); | const Controller = require('../controller'); | ||||||
| 
 | 
 | ||||||
| const extractUser = require('../../extract-user'); | const extractUser = require('../../extract-user'); | ||||||
| const { handleErrors } = require('./util'); | const { handleErrors } = require('../util'); | ||||||
| const { | const { | ||||||
|     DELETE_STRATEGY, |     DELETE_STRATEGY, | ||||||
|     CREATE_STRATEGY, |     CREATE_STRATEGY, | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import { Request, Response } from 'express'; | |||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| 
 | 
 | ||||||
| import { UPDATE_FEATURE } from '../../types/permissions'; | import { UPDATE_FEATURE } from '../../types/permissions'; | ||||||
| import { handleErrors } from './util'; | import { handleErrors } from '../util'; | ||||||
| import extractUsername from '../../extract-user'; | import extractUsername from '../../extract-user'; | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ import { Logger } from '../../logger'; | |||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| 
 | 
 | ||||||
| import { UPDATE_FEATURE } from '../../types/permissions'; | import { UPDATE_FEATURE } from '../../types/permissions'; | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import extractUsername from '../../extract-user'; | import extractUsername from '../../extract-user'; | ||||||
| 
 | 
 | ||||||
| const version = 1; | const version = 1; | ||||||
| @ -33,52 +32,32 @@ class TagController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getTags(req: Request, res: Response): Promise<void> { |     async getTags(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const tags = await this.tagService.getTags(); |         const tags = await this.tagService.getTags(); | ||||||
|         res.json({ version, tags }); |         res.json({ version, tags }); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getTagsByType(req: Request, res: Response): Promise<void> { |     async getTagsByType(req: Request, res: Response): Promise<void> { | ||||||
|         try { |  | ||||||
|         const tags = await this.tagService.getTagsByType(req.params.type); |         const tags = await this.tagService.getTagsByType(req.params.type); | ||||||
|         res.json({ version, tags }); |         res.json({ version, tags }); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getTag(req: Request, res: Response): Promise<void> { |     async getTag(req: Request, res: Response): Promise<void> { | ||||||
|         const { type, value } = req.params; |         const { type, value } = req.params; | ||||||
|         try { |  | ||||||
|         const tag = await this.tagService.getTag({ type, value }); |         const tag = await this.tagService.getTag({ type, value }); | ||||||
|         res.json({ version, tag }); |         res.json({ version, tag }); | ||||||
|         } catch (err) { |  | ||||||
|             handleErrors(res, this.logger, err); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async createTag(req: Request, res: Response): Promise<void> { |     async createTag(req: Request, res: Response): Promise<void> { | ||||||
|         const userName = extractUsername(req); |         const userName = extractUsername(req); | ||||||
|         try { |  | ||||||
|         await this.tagService.createTag(req.body, userName); |         await this.tagService.createTag(req.body, userName); | ||||||
|         res.status(201).end(); |         res.status(201).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async deleteTag(req: Request, res: Response): Promise<void> { |     async deleteTag(req: Request, res: Response): Promise<void> { | ||||||
|         const { type, value } = req.params; |         const { type, value } = req.params; | ||||||
|         const userName = extractUsername(req); |         const userName = extractUsername(req); | ||||||
|         try { |  | ||||||
|         await this.tagService.deleteTag({ type, value }, userName); |         await this.tagService.deleteTag({ type, value }, userName); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (error) { |  | ||||||
|             handleErrors(res, this.logger, error); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| export default TagController; | export default TagController; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { ADMIN } from '../../types/permissions'; | |||||||
| import UserService from '../../services/user-service'; | import UserService from '../../services/user-service'; | ||||||
| import { AccessService } from '../../services/access-service'; | import { AccessService } from '../../services/access-service'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| import { handleErrors } from './util'; | import { handleErrors } from '../util'; | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { EmailService } from '../../services/email-service'; | import { EmailService } from '../../services/email-service'; | ||||||
| import ResetTokenService from '../../services/reset-token-service'; | import ResetTokenService from '../../services/reset-token-service'; | ||||||
|  | |||||||
| @ -2,11 +2,10 @@ import { Response } from 'express'; | |||||||
| 
 | 
 | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| import { IUserRequest } from './user'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import UserFeedbackService from '../../services/user-feedback-service'; | import UserFeedbackService from '../../services/user-feedback-service'; | ||||||
| import { handleErrors } from './util'; | import { IAuthRequest } from '../unleash-types'; | ||||||
| 
 | 
 | ||||||
| interface IFeedbackBody { | interface IFeedbackBody { | ||||||
|     neverShow?: boolean; |     neverShow?: boolean; | ||||||
| @ -32,7 +31,7 @@ class UserFeedbackController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async recordFeedback( |     private async recordFeedback( | ||||||
|         req: IUserRequest<any, any, IFeedbackBody, any>, |         req: IAuthRequest<any, any, IFeedbackBody, any>, | ||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const BAD_REQUEST = 400; |         const BAD_REQUEST = 400; | ||||||
| @ -54,18 +53,12 @@ class UserFeedbackController extends Controller { | |||||||
|             neverShow: req.body.neverShow || false, |             neverShow: req.body.neverShow || false, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         try { |         const updated = await this.userFeedbackService.updateFeedback(feedback); | ||||||
|             const updated = await this.userFeedbackService.updateFeedback( |  | ||||||
|                 feedback, |  | ||||||
|             ); |  | ||||||
|         res.json(updated); |         res.json(updated); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async updateFeedbackSettings( |     private async updateFeedbackSettings( | ||||||
|         req: IUserRequest<any, any, IFeedbackBody, any>, |         req: IAuthRequest<any, any, IFeedbackBody, any>, | ||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { user } = req; |         const { user } = req; | ||||||
| @ -78,14 +71,8 @@ class UserFeedbackController extends Controller { | |||||||
|             neverShow: req.body.neverShow || false, |             neverShow: req.body.neverShow || false, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         try { |         const updated = await this.userFeedbackService.updateFeedback(feedback); | ||||||
|             const updated = await this.userFeedbackService.updateFeedback( |  | ||||||
|                 feedback, |  | ||||||
|             ); |  | ||||||
|         res.json(updated); |         res.json(updated); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,13 +1,10 @@ | |||||||
| import { Request, Response } from 'express'; | import { Response } from 'express'; | ||||||
| import { IAuthRequest } from '../unleash-types'; | import { IAuthRequest } from '../unleash-types'; | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import { AccessService } from '../../services/access-service'; | import { AccessService } from '../../services/access-service'; | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import UserService from '../../services/user-service'; | import UserService from '../../services/user-service'; | ||||||
| import User from '../../types/user'; |  | ||||||
| import { Logger } from '../../logger'; |  | ||||||
| import { handleErrors } from './util'; |  | ||||||
| import SessionService from '../../services/session-service'; | import SessionService from '../../services/session-service'; | ||||||
| import UserFeedbackService from '../../services/user-feedback-service'; | import UserFeedbackService from '../../services/user-feedback-service'; | ||||||
| 
 | 
 | ||||||
| @ -16,11 +13,6 @@ interface IChangeUserRequest { | |||||||
|     confirmPassword: string; |     confirmPassword: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IUserRequest<PARAM, QUERY, BODY, RESPONSE> |  | ||||||
|     extends Request<PARAM, QUERY, BODY, RESPONSE> { |  | ||||||
|     user: User; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class UserController extends Controller { | class UserController extends Controller { | ||||||
|     private accessService: AccessService; |     private accessService: AccessService; | ||||||
| 
 | 
 | ||||||
| @ -30,8 +22,6 @@ class UserController extends Controller { | |||||||
| 
 | 
 | ||||||
|     private sessionService: SessionService; |     private sessionService: SessionService; | ||||||
| 
 | 
 | ||||||
|     private logger: Logger; |  | ||||||
| 
 |  | ||||||
|     constructor( |     constructor( | ||||||
|         config: IUnleashConfig, |         config: IUnleashConfig, | ||||||
|         { |         { | ||||||
| @ -52,7 +42,6 @@ class UserController extends Controller { | |||||||
|         this.userService = userService; |         this.userService = userService; | ||||||
|         this.sessionService = sessionService; |         this.sessionService = sessionService; | ||||||
|         this.userFeedbackService = userFeedbackService; |         this.userFeedbackService = userFeedbackService; | ||||||
|         this.logger = config.getLogger('lib/routes/admin-api/user.ts'); |  | ||||||
| 
 | 
 | ||||||
|         this.get('/', this.getUser); |         this.get('/', this.getUser); | ||||||
|         this.post('/change-password', this.updateUserPass); |         this.post('/change-password', this.updateUserPass); | ||||||
| @ -76,12 +65,11 @@ class UserController extends Controller { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async updateUserPass( |     async updateUserPass( | ||||||
|         req: IUserRequest<any, any, IChangeUserRequest, any>, |         req: IAuthRequest<any, any, IChangeUserRequest, any>, | ||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { user } = req; |         const { user } = req; | ||||||
|         const { password, confirmPassword } = req.body; |         const { password, confirmPassword } = req.body; | ||||||
|         try { |  | ||||||
|         if (password === confirmPassword) { |         if (password === confirmPassword) { | ||||||
|             this.userService.validatePassword(password); |             this.userService.validatePassword(password); | ||||||
|             await this.userService.changePassword(user.id, password); |             await this.userService.changePassword(user.id, password); | ||||||
| @ -89,21 +77,12 @@ class UserController extends Controller { | |||||||
|         } else { |         } else { | ||||||
|             res.status(400).end(); |             res.status(400).end(); | ||||||
|         } |         } | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async mySessions(req: IAuthRequest, res: Response): Promise<void> { |     async mySessions(req: IAuthRequest, res: Response): Promise<void> { | ||||||
|         const { user } = req; |         const { user } = req; | ||||||
|         try { |         const sessions = await this.sessionService.getSessionsForUser(user.id); | ||||||
|             const sessions = await this.sessionService.getSessionsForUser( |  | ||||||
|                 user.id, |  | ||||||
|             ); |  | ||||||
|         res.json(sessions); |         res.json(sessions); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ import { Request, Response } from 'express'; | |||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import UserService from '../../services/user-service'; | import UserService from '../../services/user-service'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../../logger'; | ||||||
| import { handleErrors } from '../admin-api/util'; |  | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| 
 | 
 | ||||||
| @ -40,23 +39,15 @@ class ResetPasswordController extends Controller { | |||||||
|     async sendResetPasswordEmail(req: Request, res: Response): Promise<void> { |     async sendResetPasswordEmail(req: Request, res: Response): Promise<void> { | ||||||
|         const { email } = req.body; |         const { email } = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         await this.userService.createResetPasswordEmail(email); |         await this.userService.createResetPasswordEmail(email); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async validatePassword(req: Request, res: Response): Promise<void> { |     async validatePassword(req: Request, res: Response): Promise<void> { | ||||||
|         const { password } = req.body; |         const { password } = req.body; | ||||||
| 
 | 
 | ||||||
|         try { |  | ||||||
|         this.userService.validatePassword(password); |         this.userService.validatePassword(password); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async validateToken( |     async validateToken( | ||||||
| @ -64,13 +55,9 @@ class ResetPasswordController extends Controller { | |||||||
|         res: Response, |         res: Response, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const { token } = req.query; |         const { token } = req.query; | ||||||
|         try { |  | ||||||
|         const user = await this.userService.getUserForToken(token); |         const user = await this.userService.getUserForToken(token); | ||||||
|         await this.logout(req); |         await this.logout(req); | ||||||
|         res.status(200).json(user); |         res.status(200).json(user); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async changePassword( |     async changePassword( | ||||||
| @ -79,12 +66,8 @@ class ResetPasswordController extends Controller { | |||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         await this.logout(req); |         await this.logout(req); | ||||||
|         const { token, password } = req.body; |         const { token, password } = req.body; | ||||||
|         try { |  | ||||||
|         await this.userService.resetPassword(token, password); |         await this.userService.resetPassword(token, password); | ||||||
|         res.status(200).end(); |         res.status(200).end(); | ||||||
|         } catch (e) { |  | ||||||
|             handleErrors(res, this.logger, e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async logout(req: SessionRequest<any, any, any, any>) { |     private async logout(req: SessionRequest<any, any, any, any>) { | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| const Controller = require('../controller'); | const Controller = require('../controller'); | ||||||
| 
 | 
 | ||||||
| class PasswordProvider extends Controller { | class PasswordProvider extends Controller { | ||||||
|     constructor({ getLogger }, { userService }) { |     constructor(config, { userService }) { | ||||||
|         super(); |         super(config); | ||||||
|         this.logger = getLogger('/auth/password-provider.js'); |         this.logger = config.getLogger('/auth/password-provider.js'); | ||||||
|         this.userService = userService; |         this.userService = userService; | ||||||
| 
 | 
 | ||||||
|         this.post('/login', this.login); |         this.post('/login', this.login); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import memoizee from 'memoizee'; | import memoizee from 'memoizee'; | ||||||
| import { Request, Response } from 'express'; | import { Request, Response } from 'express'; | ||||||
| import { handleErrors } from '../admin-api/util'; | import { handleErrors } from '../util'; | ||||||
| import Controller from '../controller'; | import Controller from '../controller'; | ||||||
| import { IUnleashServices } from '../../types/services'; | import { IUnleashServices } from '../../types/services'; | ||||||
| import { IUnleashConfig } from '../../types/option'; | import { IUnleashConfig } from '../../types/option'; | ||||||
|  | |||||||
| @ -1,10 +1,24 @@ | |||||||
| import { IRouter } from 'express'; | import { IRouter, Request, Response } from 'express'; | ||||||
|  | import { Logger } from 'lib/logger'; | ||||||
| import { IUnleashConfig } from '../types/option'; | import { IUnleashConfig } from '../types/option'; | ||||||
|  | import { handleErrors } from './util'; | ||||||
| 
 | 
 | ||||||
| const { Router } = require('express'); | const { Router } = require('express'); | ||||||
| const NoAccessError = require('../error/no-access-error'); | const NoAccessError = require('../error/no-access-error'); | ||||||
| const requireContentType = require('../middleware/content_type_checker'); | const requireContentType = require('../middleware/content_type_checker'); | ||||||
| 
 | 
 | ||||||
|  | interface IRequestHandler< | ||||||
|  |     P = any, | ||||||
|  |     ResBody = any, | ||||||
|  |     ReqBody = any, | ||||||
|  |     ReqQuery = any, | ||||||
|  | > { | ||||||
|  |     ( | ||||||
|  |         req: Request<P, ResBody, ReqBody, ReqQuery>, | ||||||
|  |         res: Response<ResBody>, | ||||||
|  |     ): Promise<void> | void; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const checkPermission = (permission) => async (req, res, next) => { | const checkPermission = (permission) => async (req, res, next) => { | ||||||
|     if (!permission) { |     if (!permission) { | ||||||
|         return next(); |         return next(); | ||||||
| @ -17,24 +31,48 @@ const checkPermission = (permission) => async (req, res, next) => { | |||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Base class for Controllers to standardize binding to express Router. |  * Base class for Controllers to standardize binding to express Router. | ||||||
|  |  * | ||||||
|  |  * This class will take care of the following: | ||||||
|  |  * - try/catch inside RequestHandler | ||||||
|  |  * - await if the RequestHandler returns a promise. | ||||||
|  |  * - access control | ||||||
|  */ |  */ | ||||||
| export default class Controller { | export default class Controller { | ||||||
|  |     private ownLogger: Logger; | ||||||
|  | 
 | ||||||
|     app: IRouter; |     app: IRouter; | ||||||
| 
 | 
 | ||||||
|     config: IUnleashConfig; |     config: IUnleashConfig; | ||||||
| 
 | 
 | ||||||
|     constructor(config: IUnleashConfig) { |     constructor(config: IUnleashConfig) { | ||||||
|  |         this.ownLogger = config.getLogger( | ||||||
|  |             `controller/${this.constructor.name}`, | ||||||
|  |         ); | ||||||
|         this.app = Router(); |         this.app = Router(); | ||||||
|         this.config = config; |         this.config = config; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get(path: string, handler: Function, permission?: string): void { |     wrap(handler: IRequestHandler): IRequestHandler { | ||||||
|         this.app.get(path, checkPermission(permission), handler.bind(this)); |         return async (req: Request, res: Response) => { | ||||||
|  |             try { | ||||||
|  |                 await handler(req, res); | ||||||
|  |             } catch (error) { | ||||||
|  |                 handleErrors(res, this.ownLogger, error); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     get(path: string, handler: IRequestHandler, permission?: string): void { | ||||||
|  |         this.app.get( | ||||||
|  |             path, | ||||||
|  |             checkPermission(permission), | ||||||
|  |             this.wrap(handler.bind(this)), | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     post( |     post( | ||||||
|         path: string, |         path: string, | ||||||
|         handler: Function, |         handler: IRequestHandler, | ||||||
|         permission?: string, |         permission?: string, | ||||||
|         ...acceptedContentTypes: string[] |         ...acceptedContentTypes: string[] | ||||||
|     ): void { |     ): void { | ||||||
| @ -42,13 +80,13 @@ export default class Controller { | |||||||
|             path, |             path, | ||||||
|             checkPermission(permission), |             checkPermission(permission), | ||||||
|             requireContentType(...acceptedContentTypes), |             requireContentType(...acceptedContentTypes), | ||||||
|             handler.bind(this), |             this.wrap(handler.bind(this)), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     put( |     put( | ||||||
|         path: string, |         path: string, | ||||||
|         handler: Function, |         handler: IRequestHandler, | ||||||
|         permission?: string, |         permission?: string, | ||||||
|         ...acceptedContentTypes: string[] |         ...acceptedContentTypes: string[] | ||||||
|     ): void { |     ): void { | ||||||
| @ -56,17 +94,21 @@ export default class Controller { | |||||||
|             path, |             path, | ||||||
|             checkPermission(permission), |             checkPermission(permission), | ||||||
|             requireContentType(...acceptedContentTypes), |             requireContentType(...acceptedContentTypes), | ||||||
|             handler.bind(this), |             this.wrap(handler.bind(this)), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     delete(path: string, handler: Function, permission?: string): void { |     delete(path: string, handler: IRequestHandler, permission?: string): void { | ||||||
|         this.app.delete(path, checkPermission(permission), handler.bind(this)); |         this.app.delete( | ||||||
|  |             path, | ||||||
|  |             checkPermission(permission), | ||||||
|  |             this.wrap(handler.bind(this)), | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fileupload( |     fileupload( | ||||||
|         path: string, |         path: string, | ||||||
|         filehandler: Function, |         filehandler: IRequestHandler, | ||||||
|         handler: Function, |         handler: Function, | ||||||
|         permission?: string, |         permission?: string, | ||||||
|     ): void { |     ): void { | ||||||
| @ -74,11 +116,10 @@ export default class Controller { | |||||||
|             path, |             path, | ||||||
|             checkPermission(permission), |             checkPermission(permission), | ||||||
|             filehandler.bind(this), |             filehandler.bind(this), | ||||||
|             handler.bind(this), |             this.wrap(handler.bind(this)), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 |  | ||||||
|     use(path: string, router: IRouter): void { |     use(path: string, router: IRouter): void { | ||||||
|         this.app.use(path, router); |         this.app.use(path, router); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ class HealthCheckController extends Controller { | |||||||
|         config: IUnleashConfig, |         config: IUnleashConfig, | ||||||
|         { healthService }: Pick<IUnleashServices, 'healthService'>, |         { healthService }: Pick<IUnleashServices, 'healthService'>, | ||||||
|     ) { |     ) { | ||||||
|         super(); |         super(config); | ||||||
|         this.logger = config.getLogger('health-check.js'); |         this.logger = config.getLogger('health-check.js'); | ||||||
|         this.healthService = healthService; |         this.healthService = healthService; | ||||||
|         this.get('/', (req, res) => this.index(req, res)); |         this.get('/', (req, res) => this.index(req, res)); | ||||||
|  | |||||||
| @ -1,7 +1,13 @@ | |||||||
| import { Request } from 'express'; | import { Request } from 'express'; | ||||||
|  | import * as core from 'express-serve-static-core'; | ||||||
| import User from '../types/user'; | import User from '../types/user'; | ||||||
| 
 | 
 | ||||||
| export interface IAuthRequest extends Request { | export interface IAuthRequest< | ||||||
|  |     PARAM = core.ParamsDictionary, | ||||||
|  |     ResBody = any, | ||||||
|  |     ReqBody = any, | ||||||
|  |     ReqQuery = core.Query, | ||||||
|  | > extends Request<PARAM, ResBody, ReqBody, ReqQuery> { | ||||||
|     user: User; |     user: User; | ||||||
|     logout: () => void; |     logout: () => void; | ||||||
|     session: any; |     session: any; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import joi from 'joi'; | import joi from 'joi'; | ||||||
| import { Response } from 'express'; | import { Response } from 'express'; | ||||||
| import { Logger } from '../../logger'; | import { Logger } from '../logger'; | ||||||
| 
 | 
 | ||||||
| export const customJoi = joi.extend((j) => ({ | export const customJoi = joi.extend((j) => ({ | ||||||
|     type: 'isUrlFriendly', |     type: 'isUrlFriendly', | ||||||
| @ -1,5 +1,5 @@ | |||||||
| import joi from 'joi'; | import joi from 'joi'; | ||||||
| import { nameType } from '../routes/admin-api/util'; | import { nameType } from '../routes/util'; | ||||||
| 
 | 
 | ||||||
| export const nameSchema = joi | export const nameSchema = joi | ||||||
|     .object() |     .object() | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ import User from './types/user'; | |||||||
| 
 | 
 | ||||||
| import * as permissions from './types/permissions'; | import * as permissions from './types/permissions'; | ||||||
| import AuthenticationRequired from './types/authentication-required'; | import AuthenticationRequired from './types/authentication-required'; | ||||||
|  | import Controller from './routes/controller'; | ||||||
| import * as eventType from './types/events'; | import * as eventType from './types/events'; | ||||||
| import { addEventHook } from './event-hook'; | import { addEventHook } from './event-hook'; | ||||||
| import registerGracefulShutdown from './util/graceful-shutdown'; | import registerGracefulShutdown from './util/graceful-shutdown'; | ||||||
| @ -145,6 +146,7 @@ const serverImpl = { | |||||||
|     create, |     create, | ||||||
|     User, |     User, | ||||||
|     AuthenticationRequired, |     AuthenticationRequired, | ||||||
|  |     Controller, | ||||||
|     permissions, |     permissions, | ||||||
|     eventType, |     eventType, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import joi from 'joi'; | import joi from 'joi'; | ||||||
| import { nameType } from '../routes/admin-api/util'; | import { nameType } from '../routes/util'; | ||||||
| 
 | 
 | ||||||
| export const addonSchema = joi | export const addonSchema = joi | ||||||
|     .object() |     .object() | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| const joi = require('joi'); | const joi = require('joi'); | ||||||
| const { nameType } = require('../routes/admin-api/util'); | const { nameType } = require('../routes/util'); | ||||||
| 
 | 
 | ||||||
| const nameSchema = joi.object().keys({ name: nameType }); | const nameSchema = joi.object().keys({ name: nameType }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| const joi = require('joi'); | const joi = require('joi'); | ||||||
| const { nameType } = require('../routes/admin-api/util'); | const { nameType } = require('../routes/util'); | ||||||
| 
 | 
 | ||||||
| const projectSchema = joi | const projectSchema = joi | ||||||
|     .object() |     .object() | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import User from '../types/user'; | |||||||
| import { AccessService } from './access-service'; | import { AccessService } from './access-service'; | ||||||
| import NameExistsError from '../error/name-exists-error'; | import NameExistsError from '../error/name-exists-error'; | ||||||
| import InvalidOperationError from '../error/invalid-operation-error'; | import InvalidOperationError from '../error/invalid-operation-error'; | ||||||
| import { nameType } from '../routes/admin-api/util'; | import { nameType } from '../routes/util'; | ||||||
| import schema from './project-schema'; | import schema from './project-schema'; | ||||||
| import NotFoundError from '../error/notfound-error'; | import NotFoundError from '../error/notfound-error'; | ||||||
| import { | import { | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import strategySchema from './strategy-schema'; | |||||||
| import { tagSchema } from './tag-schema'; | import { tagSchema } from './tag-schema'; | ||||||
| import { tagTypeSchema } from './tag-type-schema'; | import { tagTypeSchema } from './tag-type-schema'; | ||||||
| import projectSchema from './project-schema'; | import projectSchema from './project-schema'; | ||||||
| import { nameType } from '../routes/admin-api/util'; | import { nameType } from '../routes/util'; | ||||||
| 
 | 
 | ||||||
| export const featureStrategySchema = joi | export const featureStrategySchema = joi | ||||||
|     .object() |     .object() | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| const joi = require('joi'); | const joi = require('joi'); | ||||||
| const { nameType } = require('../routes/admin-api/util'); | const { nameType } = require('../routes/util'); | ||||||
| 
 | 
 | ||||||
| const strategySchema = joi | const strategySchema = joi | ||||||
|     .object() |     .object() | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import Joi from 'joi'; | import Joi from 'joi'; | ||||||
| 
 | 
 | ||||||
| import { customJoi } from '../routes/admin-api/util'; | import { customJoi } from '../routes/util'; | ||||||
| 
 | 
 | ||||||
| export const tagSchema = Joi.object() | export const tagSchema = Joi.object() | ||||||
|     .keys({ |     .keys({ | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import Joi from 'joi'; | import Joi from 'joi'; | ||||||
| import { customJoi } from '../routes/admin-api/util'; | import { customJoi } from '../routes/util'; | ||||||
| 
 | 
 | ||||||
| export const tagTypeSchema = Joi.object() | export const tagTypeSchema = Joi.object() | ||||||
|     .keys({ |     .keys({ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user