diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc8d690f13..72bf74f759 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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: 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. \ No newline at end of file diff --git a/src/lib/addons/addon-schema.ts b/src/lib/addons/addon-schema.ts index 78ea935c3d..540060e6a5 100644 --- a/src/lib/addons/addon-schema.ts +++ b/src/lib/addons/addon-schema.ts @@ -1,5 +1,5 @@ import joi from 'joi'; -import { nameType } from '../routes/admin-api/util'; +import { nameType } from '../routes/util'; import { tagTypeSchema } from '../services/tag-type-schema'; export const addonDefinitionSchema = joi.object().keys({ diff --git a/src/lib/middleware/no-authentication.test.ts b/src/lib/middleware/no-authentication.test.ts index 9466191372..420f774511 100644 --- a/src/lib/middleware/no-authentication.test.ts +++ b/src/lib/middleware/no-authentication.test.ts @@ -1,14 +1,14 @@ +import { IAuthRequest } from 'lib/routes/unleash-types'; import supertest from 'supertest'; import express from 'express'; import noAuthentication from './no-authentication'; -import { IUserRequest } from '../routes/admin-api/user'; test('should add dummy user object to all requests', () => { expect.assertions(1); const app = express(); noAuthentication('', app); - app.get('/api/admin/test', (req: IUserRequest, res) => { + app.get('/api/admin/test', (req: IAuthRequest, res) => { const user = { ...req.user }; return res.status(200).json(user).end(); diff --git a/src/lib/routes/admin-api/addon.ts b/src/lib/routes/admin-api/addon.ts index 73b6bfa3ba..a549c9636e 100644 --- a/src/lib/routes/admin-api/addon.ts +++ b/src/lib/routes/admin-api/addon.ts @@ -6,7 +6,6 @@ import { Logger } from '../../logger'; import AddonService from '../../services/addon-service'; import extractUser from '../../extract-user'; -import { handleErrors } from './util'; import { CREATE_ADDON, UPDATE_ADDON, @@ -34,13 +33,9 @@ class AddonController extends Controller { } async getAddons(req: Request, res: Response): Promise { - try { - const addons = await this.addonService.getAddons(); - const providers = this.addonService.getProviderDefinitions(); - res.json({ addons, providers }); - } catch (error) { - handleErrors(res, this.logger, error); - } + const addons = await this.addonService.getAddons(); + const providers = this.addonService.getProviderDefinitions(); + res.json({ addons, providers }); } async getAddon( @@ -48,12 +43,8 @@ class AddonController extends Controller { res: Response, ): Promise { const { id } = req.params; - try { - const addon = await this.addonService.getAddon(id); - res.json(addon); - } catch (error) { - handleErrors(res, this.logger, error); - } + const addon = await this.addonService.getAddon(id); + res.json(addon); } async updateAddon( @@ -64,27 +55,15 @@ class AddonController extends Controller { const createdBy = extractUser(req); const data = req.body; - try { - const addon = await this.addonService.updateAddon( - id, - data, - createdBy, - ); - res.status(200).json(addon); - } catch (error) { - handleErrors(res, this.logger, error); - } + const addon = await this.addonService.updateAddon(id, data, createdBy); + res.status(200).json(addon); } async createAddon(req: Request, res: Response): Promise { const createdBy = extractUser(req); const data = req.body; - try { - const addon = await this.addonService.createAddon(data, createdBy); - res.status(201).json(addon); - } catch (error) { - handleErrors(res, this.logger, error); - } + const addon = await this.addonService.createAddon(data, createdBy); + res.status(201).json(addon); } async deleteAddon( @@ -93,12 +72,8 @@ class AddonController extends Controller { ): Promise { const { id } = req.params; const username = extractUser(req); - try { - await this.addonService.removeAddon(id, username); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.addonService.removeAddon(id, username); + res.status(200).end(); } } export default AddonController; diff --git a/src/lib/routes/admin-api/archive.ts b/src/lib/routes/admin-api/archive.ts index 5a57837e80..e8d62ffa7e 100644 --- a/src/lib/routes/admin-api/archive.ts +++ b/src/lib/routes/admin-api/archive.ts @@ -1,5 +1,4 @@ import { Request, Response } from 'express'; -import { handleErrors } from './util'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; import { Logger } from '../../logger'; @@ -36,13 +35,10 @@ export default class ArchiveController extends Controller { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getArchivedFeatures(req, res): Promise { - try { - const features = - await this.featureService.getMetadataForAllFeatures(true); - res.json({ version: 2, features }); - } catch (err) { - handleErrors(res, this.logger, err); - } + const features = await this.featureService.getMetadataForAllFeatures( + true, + ); + res.json({ version: 2, features }); } async deleteFeature( @@ -51,25 +47,16 @@ export default class ArchiveController extends Controller { ): Promise { const { featureName } = req.params; const user = extractUser(req); - try { - await this.featureService.deleteFeature(featureName, user); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.featureService.deleteFeature(featureName, user); + res.status(200).end(); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async reviveFeatureToggle(req, res): Promise { const userName = extractUser(req); const { featureName } = req.params; - - try { - await this.featureService.reviveToggle(featureName, userName); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.featureService.reviveToggle(featureName, userName); + res.status(200).end(); } } diff --git a/src/lib/routes/admin-api/context.ts b/src/lib/routes/admin-api/context.ts index 62be89daed..e1d3136bdb 100644 --- a/src/lib/routes/admin-api/context.ts +++ b/src/lib/routes/admin-api/context.ts @@ -2,7 +2,6 @@ import { Request, Response } from 'express'; import Controller from '../controller'; -import { handleErrors } from './util'; import extractUser from '../../extract-user'; import { @@ -45,12 +44,8 @@ class ContextController extends Controller { } async getContextFields(req: Request, res: Response): Promise { - try { - const fields = await this.contextService.getAll(); - res.status(200).json(fields).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + const fields = await this.contextService.getAll(); + res.status(200).json(fields).end(); } async getContextField(req: Request, res: Response): Promise { @@ -69,12 +64,8 @@ class ContextController extends Controller { const value = req.body; const userName = extractUser(req); - try { - await this.contextService.createContextField(value, userName); - res.status(201).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.contextService.createContextField(value, userName); + res.status(201).end(); } async updateContextField(req: Request, res: Response): Promise { @@ -84,38 +75,23 @@ class ContextController extends Controller { contextField.name = name; - try { - await this.contextService.updateContextField( - contextField, - userName, - ); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.contextService.updateContextField(contextField, userName); + res.status(200).end(); } async deleteContextField(req: Request, res: Response): Promise { const name = req.params.contextField; const userName = extractUser(req); - try { - await this.contextService.deleteContextField(name, userName); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.contextService.deleteContextField(name, userName); + res.status(200).end(); } async validate(req: Request, res: Response): Promise { const { name } = req.body; - try { - await this.contextService.validateName(name); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.contextService.validateName(name); + res.status(200).end(); } } export default ContextController; diff --git a/src/lib/routes/admin-api/email.ts b/src/lib/routes/admin-api/email.ts index 811b9162e5..9f8dae44bf 100644 --- a/src/lib/routes/admin-api/email.ts +++ b/src/lib/routes/admin-api/email.ts @@ -1,6 +1,5 @@ import { ADMIN } from '../../types/permissions'; import { TemplateFormat } from '../../services/email-service'; -import { handleErrors } from './util'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; @@ -20,40 +19,32 @@ export default class EmailController extends Controller { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getHtmlPreview(req, res): Promise { - try { - const { template } = req.params; - const ctx = req.query; - const data = await this.emailService.compileTemplate( - template, - TemplateFormat.HTML, - ctx, - ); - res.setHeader('Content-Type', 'text/html'); - res.status(200); - res.send(data); - res.end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + const { template } = req.params; + const ctx = req.query; + const data = await this.emailService.compileTemplate( + template, + TemplateFormat.HTML, + ctx, + ); + res.setHeader('Content-Type', 'text/html'); + res.status(200); + res.send(data); + res.end(); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getTextPreview(req, res) { - try { - const { template } = req.params; - const ctx = req.query; - const data = await this.emailService.compileTemplate( - template, - TemplateFormat.PLAIN, - ctx, - ); - res.setHeader('Content-Type', 'text/plain'); - res.status(200); - res.send(data); - res.end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + const { template } = req.params; + const ctx = req.query; + const data = await this.emailService.compileTemplate( + template, + TemplateFormat.PLAIN, + ctx, + ); + res.setHeader('Content-Type', 'text/plain'); + res.status(200); + res.send(data); + res.end(); } } module.exports = EmailController; diff --git a/src/lib/routes/admin-api/environments-controller.ts b/src/lib/routes/admin-api/environments-controller.ts index d4af01ab3c..69944eaa41 100644 --- a/src/lib/routes/admin-api/environments-controller.ts +++ b/src/lib/routes/admin-api/environments-controller.ts @@ -5,7 +5,7 @@ import { IUnleashConfig } from '../../types/option'; import { IEnvironment } from '../../types/model'; import EnvironmentService from '../../services/environment-service'; import { Logger } from '../../logger'; -import { handleErrors } from './util'; +import { handleErrors } from '../util'; import { ADMIN } from '../../types/permissions'; interface EnvironmentParam { diff --git a/src/lib/routes/admin-api/event.ts b/src/lib/routes/admin-api/event.ts index d025011d4a..e2baf5bc78 100644 --- a/src/lib/routes/admin-api/event.ts +++ b/src/lib/routes/admin-api/event.ts @@ -1,4 +1,3 @@ -import { handleErrors } from './util'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; import EventService from '../../services/event-service'; @@ -25,31 +24,21 @@ export default class EventController extends Controller { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getEvents(req, res): Promise { - try { - const events = await this.eventService.getEvents(); - eventDiffer.addDiffs(events); - res.json({ version, events }); - } catch (e) { - handleErrors(res, this.logger, e); - } + const events = await this.eventService.getEvents(); + eventDiffer.addDiffs(events); + res.json({ version, events }); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async getEventsForToggle(req, res): Promise { const toggleName = req.params.name; - try { - const events = await this.eventService.getEventsForToggle( - toggleName, - ); + const events = await this.eventService.getEventsForToggle(toggleName); - if (events) { - eventDiffer.addDiffs(events); - res.json({ toggleName, events }); - } else { - res.status(404).json({ error: 'Could not find events' }); - } - } catch (e) { - handleErrors(res, this.logger, e); + if (events) { + eventDiffer.addDiffs(events); + res.json({ toggleName, events }); + } else { + res.status(404).json({ error: 'Could not find events' }); } } } diff --git a/src/lib/routes/admin-api/feature-type.ts b/src/lib/routes/admin-api/feature-type.ts index 2e0751231e..cb7a16f210 100644 --- a/src/lib/routes/admin-api/feature-type.ts +++ b/src/lib/routes/admin-api/feature-type.ts @@ -1,5 +1,4 @@ import { Request, Response } from 'express'; -import { handleErrors } from './util'; import { IUnleashServices } from '../../types/services'; import FeatureTypeService from '../../services/feature-type-service'; import { Logger } from '../../logger'; @@ -26,12 +25,8 @@ export default class FeatureTypeController extends Controller { } async getAllFeatureTypes(req: Request, res: Response): Promise { - try { - const types = await this.featureTypeService.getAll(); - res.json({ version, types }); - } catch (e) { - handleErrors(res, this.logger, e); - } + const types = await this.featureTypeService.getAll(); + res.json({ version, types }); } } diff --git a/src/lib/routes/admin-api/feature.ts b/src/lib/routes/admin-api/feature.ts index dd55dbfc55..b493f732c4 100644 --- a/src/lib/routes/admin-api/feature.ts +++ b/src/lib/routes/admin-api/feature.ts @@ -3,7 +3,6 @@ import { Request, Response } from 'express'; import Controller from '../controller'; -import { handleErrors } from './util'; import extractUser from '../../extract-user'; import { UPDATE_FEATURE, @@ -94,129 +93,95 @@ class FeatureController extends Controller { async getAllToggles(req: Request, res: Response): Promise { 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 }); - } catch (err) { - handleErrors(res, this.logger, err); - } + res.json({ version, features }); } async getToggle( req: Request<{ featureName: string }, any, any, any>, res: Response, ): Promise { - try { - const name = req.params.featureName; - const feature = await this.featureService2.getFeatureToggle(name); - const strategies = - feature.environments.find((e) => e.name === GLOBAL_ENV) - ?.strategies || []; - res.json({ - ...feature, - strategies, - }).end(); - } catch (err) { - handleErrors(res, this.logger, err); - } + const name = req.params.featureName; + const feature = await this.featureService2.getFeatureToggle(name); + const strategies = + feature.environments.find((e) => e.name === GLOBAL_ENV) + ?.strategies || []; + res.json({ + ...feature, + strategies, + }).end(); } - // TODO async listTags(req: Request, res: Response): Promise { - try { - const tags = await this.featureTagService.listTags( - req.params.featureName, - ); - res.json({ version, tags }); - } catch (err) { - handleErrors(res, this.logger, err); - } + const tags = await this.featureTagService.listTags( + req.params.featureName, + ); + res.json({ version, tags }); } - // TODO async addTag(req: Request, res: Response): Promise { const { featureName } = req.params; const userName = extractUser(req); - try { - const tag = await this.featureTagService.addTag( - featureName, - req.body, - userName, - ); - res.status(201).json(tag); - } catch (err) { - handleErrors(res, this.logger, err); - } + const tag = await this.featureTagService.addTag( + featureName, + req.body, + userName, + ); + res.status(201).json(tag); } // TODO async removeTag(req: Request, res: Response): Promise { const { featureName, type, value } = req.params; const userName = extractUser(req); - try { - await this.featureTagService.removeTag( - featureName, - { type, value }, - userName, - ); - res.status(200).end(); - } catch (err) { - handleErrors(res, this.logger, err); - } + await this.featureTagService.removeTag( + featureName, + { type, value }, + userName, + ); + res.status(200).end(); } async validate(req: Request, res: Response): Promise { const { name } = req.body; - try { - await this.featureService2.validateName(name); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.featureService2.validateName(name); + res.status(200).end(); } async createToggle(req: Request, res: Response): Promise { const userName = extractUser(req); const toggle = req.body; - try { - const validatedToggle = await featureSchema.validateAsync(toggle); - const { enabled } = validatedToggle; - const createdFeature = - await this.featureService2.createFeatureToggle( - validatedToggle.project, - validatedToggle, - userName, - ); - const strategies = await Promise.all( - toggle.strategies.map(async (s) => - this.featureService2.createStrategy( - s, - createdFeature.project, - createdFeature.name, - ), + const validatedToggle = await featureSchema.validateAsync(toggle); + const { enabled } = validatedToggle; + const createdFeature = await this.featureService2.createFeatureToggle( + validatedToggle.project, + validatedToggle, + userName, + ); + const strategies = await Promise.all( + toggle.strategies.map(async (s) => + this.featureService2.createStrategy( + s, + createdFeature.project, + createdFeature.name, ), - ); - await this.featureService2.updateEnabled( - validatedToggle.name, - GLOBAL_ENV, - enabled, - userName, - ); + ), + ); + await this.featureService2.updateEnabled( + validatedToggle.name, + GLOBAL_ENV, + enabled, + userName, + ); - res.status(201).json({ - ...createdFeature, - enabled, - strategies, - }); - } catch (error) { - this.logger.warn(error); - handleErrors(res, this.logger, error); - } + res.status(201).json({ + ...createdFeature, + enabled, + strategies, + }); } async updateToggle(req: Request, res: Response): Promise { @@ -230,48 +195,42 @@ class FeatureController extends Controller { featureName, ); if (featureToggleExists) { - try { - await this.featureService2.getFeature(featureName); - const projectId = await this.featureService2.getProjectId( - updatedFeature.name, - ); - const value = await featureSchema.validateAsync(updatedFeature); - const { enabled } = value; - const updatedToggle = this.featureService2.updateFeatureToggle( - projectId, - value, - userName, - ); + await this.featureService2.getFeature(featureName); + const projectId = await this.featureService2.getProjectId( + updatedFeature.name, + ); + const value = await featureSchema.validateAsync(updatedFeature); + const { enabled } = value; + const updatedToggle = this.featureService2.updateFeatureToggle( + projectId, + value, + userName, + ); - await this.featureService2.removeAllStrategiesForEnv( - featureName, - ); - let strategies; - if (updatedFeature.strategies) { - strategies = await Promise.all( - updatedFeature.strategies.map(async (s) => - this.featureService2.createStrategy( - s, - projectId, - featureName, - ), + await this.featureService2.removeAllStrategiesForEnv(featureName); + let strategies; + if (updatedFeature.strategies) { + strategies = await Promise.all( + updatedFeature.strategies.map(async (s) => + this.featureService2.createStrategy( + s, + projectId, + featureName, ), - ); - } - await this.featureService2.updateEnabled( - updatedFeature.name, - GLOBAL_ENV, - updatedFeature.enabled, - userName, + ), ); - res.status(200).json({ - ...updatedToggle, - enabled, - strategies: strategies || [], - }); - } catch (error) { - handleErrors(res, this.logger, error); } + await this.featureService2.updateEnabled( + updatedFeature.name, + GLOBAL_ENV, + updatedFeature.enabled, + userName, + ); + res.status(200).json({ + ...updatedToggle, + enabled, + strategies: strategies || [], + }); } else { res.status(404) .json({ @@ -285,91 +244,67 @@ class FeatureController extends Controller { // Kept to keep backward compatibility async toggle(req: Request, res: Response): Promise { const userName = extractUser(req); - try { - const name = req.params.featureName; - const feature = await this.featureService2.toggle( - name, - GLOBAL_ENV, - userName, - ); - res.status(200).json(feature); - } catch (error) { - handleErrors(res, this.logger, error); - } + const name = req.params.featureName; + const feature = await this.featureService2.toggle( + name, + GLOBAL_ENV, + userName, + ); + res.status(200).json(feature); } async toggleOn(req: Request, res: Response): Promise { const { featureName } = req.params; const userName = extractUser(req); - try { - const feature = await this.featureService2.updateEnabled( - featureName, - GLOBAL_ENV, - true, - userName, - ); - res.json(feature); - } catch (error) { - handleErrors(res, this.logger, error); - } + const feature = await this.featureService2.updateEnabled( + featureName, + GLOBAL_ENV, + true, + userName, + ); + res.json(feature); } async toggleOff(req: Request, res: Response): Promise { const { featureName } = req.params; const userName = extractUser(req); - try { - const feature = await this.featureService2.updateEnabled( - featureName, - GLOBAL_ENV, - false, - userName, - ); - res.json(feature); - } catch (error) { - handleErrors(res, this.logger, error); - } + const feature = await this.featureService2.updateEnabled( + featureName, + GLOBAL_ENV, + false, + userName, + ); + res.json(feature); } async staleOn(req: Request, res: Response): Promise { - try { - const { featureName } = req.params; - const userName = extractUser(req); - const feature = await this.featureService2.updateStale( - featureName, - true, - userName, - ); - res.json(feature).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + const { featureName } = req.params; + const userName = extractUser(req); + const feature = await this.featureService2.updateStale( + featureName, + true, + userName, + ); + res.json(feature).end(); } async staleOff(req: Request, res: Response): Promise { - try { - const { featureName } = req.params; - const userName = extractUser(req); - const feature = await this.featureService2.updateStale( - featureName, - false, - userName, - ); - res.json(feature).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + const { featureName } = req.params; + const userName = extractUser(req); + const feature = await this.featureService2.updateStale( + featureName, + false, + userName, + ); + res.json(feature).end(); } async archiveToggle(req: Request, res: Response): Promise { const { featureName } = req.params; const userName = extractUser(req); - try { - await this.featureService2.archiveToggle(featureName, userName); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.featureService2.archiveToggle(featureName, userName); + res.status(200).end(); } } export default FeatureController; diff --git a/src/lib/routes/admin-api/metrics.ts b/src/lib/routes/admin-api/metrics.ts index 97b131db15..fd0aab6c0f 100644 --- a/src/lib/routes/admin-api/metrics.ts +++ b/src/lib/routes/admin-api/metrics.ts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; import Controller from '../controller'; -import { handleErrors } from './util'; +import { handleErrors } from '../util'; import { UPDATE_APPLICATION } from '../../types/permissions'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; diff --git a/src/lib/routes/admin-api/project/environments.ts b/src/lib/routes/admin-api/project/environments.ts index 9c5b0c3ccd..da1b1d7ea0 100644 --- a/src/lib/routes/admin-api/project/environments.ts +++ b/src/lib/routes/admin-api/project/environments.ts @@ -4,7 +4,7 @@ import { IUnleashConfig } from '../../../types/option'; import { IUnleashServices } from '../../../types/services'; import { Logger } from '../../../logger'; import EnvironmentService from '../../../services/environment-service'; -import { handleErrors } from '../util'; +import { handleErrors } from '../../util'; import { UPDATE_PROJECT } from '../../../types/permissions'; const PREFIX = '/:projectId/environments'; diff --git a/src/lib/routes/admin-api/project/features.ts b/src/lib/routes/admin-api/project/features.ts index 8c1ce14dd6..4f331f77fd 100644 --- a/src/lib/routes/admin-api/project/features.ts +++ b/src/lib/routes/admin-api/project/features.ts @@ -10,7 +10,6 @@ import { IConstraint, IStrategyConfig, } from '../../../types/model'; -import { handleErrors } from '../util'; import extractUsername from '../../../extract-user'; import ProjectHealthService from '../../../services/project-health-service'; @@ -111,14 +110,10 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { projectId } = req.params; - try { - const features = await this.featureService.getFeatureToggles({ - project: [projectId], - }); - res.json({ version: 1, features }); - } catch (e) { - handleErrors(res, this.logger, e); - } + const features = await this.featureService.getFeatureToggles({ + project: [projectId], + }); + res.json({ version: 1, features }); } async createFeatureToggle( @@ -126,17 +121,13 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { projectId } = req.params; - try { - const userName = extractUsername(req); - const created = await this.featureService.createFeatureToggle( - projectId, - req.body, - userName, - ); - res.status(201).json(created); - } catch (e) { - handleErrors(res, this.logger, e); - } + const userName = extractUsername(req); + const created = await this.featureService.createFeatureToggle( + projectId, + req.body, + userName, + ); + res.status(201).json(created); } async getEnvironment( @@ -144,17 +135,12 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { environment, featureName, projectId } = req.params; - try { - const environmentInfo = - await this.featureService.getEnvironmentInfo( - projectId, - environment, - featureName, - ); - res.status(200).json(environmentInfo); - } catch (e) { - handleErrors(res, this.logger, e); - } + const environmentInfo = await this.featureService.getEnvironmentInfo( + projectId, + environment, + featureName, + ); + res.status(200).json(environmentInfo); } async getFeature( @@ -162,12 +148,8 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { featureName } = req.params; - try { - const feature = await this.featureService.getFeature(featureName); - res.status(200).json(feature); - } catch (e) { - handleErrors(res, this.logger, e); - } + const feature = await this.featureService.getFeature(featureName); + res.status(200).json(feature); } async toggleEnvironmentOn( @@ -175,17 +157,13 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { featureName, environment } = req.params; - try { - await this.featureService.updateEnabled( - featureName, - environment, - true, - extractUsername(req), - ); - res.status(200).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + await this.featureService.updateEnabled( + featureName, + environment, + true, + extractUsername(req), + ); + res.status(200).end(); } async toggleEnvironmentOff( @@ -193,17 +171,13 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { featureName, environment } = req.params; - try { - await this.featureService.updateEnabled( - featureName, - environment, - false, - extractUsername(req), - ); - res.status(200).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + await this.featureService.updateEnabled( + featureName, + environment, + false, + extractUsername(req), + ); + res.status(200).end(); } async createFeatureStrategy( @@ -211,17 +185,13 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { projectId, featureName, environment } = req.params; - try { - const featureStrategy = await this.featureService.createStrategy( - req.body, - projectId, - featureName, - environment, - ); - res.status(200).json(featureStrategy); - } catch (e) { - handleErrors(res, this.logger, e); - } + const featureStrategy = await this.featureService.createStrategy( + req.body, + projectId, + featureName, + environment, + ); + res.status(200).json(featureStrategy); } async getFeatureStrategies( @@ -229,17 +199,13 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { projectId, featureName, environment } = req.params; - try { - const featureStrategies = - await this.featureService.getStrategiesForEnvironment( - projectId, - featureName, - environment, - ); - res.status(200).json(featureStrategies); - } catch (e) { - handleErrors(res, this.logger, e); - } + const featureStrategies = + await this.featureService.getStrategiesForEnvironment( + projectId, + featureName, + environment, + ); + res.status(200).json(featureStrategies); } async updateStrategy( @@ -247,15 +213,11 @@ export default class ProjectFeaturesController extends Controller { res: Response, ): Promise { const { strategyId } = req.params; - try { - const updatedStrategy = await this.featureService.updateStrategy( - strategyId, - req.body, - ); - res.status(200).json(updatedStrategy); - } catch (e) { - handleErrors(res, this.logger, e); - } + const updatedStrategy = await this.featureService.updateStrategy( + strategyId, + req.body, + ); + res.status(200).json(updatedStrategy); } async getStrategy( @@ -265,11 +227,7 @@ export default class ProjectFeaturesController extends Controller { this.logger.info('Getting strategy'); const { strategyId } = req.params; this.logger.info(strategyId); - try { - const strategy = await this.featureService.getStrategy(strategyId); - res.status(200).json(strategy); - } catch (e) { - handleErrors(res, this.logger, e); - } + const strategy = await this.featureService.getStrategy(strategyId); + res.status(200).json(strategy); } } diff --git a/src/lib/routes/admin-api/project/health-report.ts b/src/lib/routes/admin-api/project/health-report.ts index b62addb4da..4048bd427d 100644 --- a/src/lib/routes/admin-api/project/health-report.ts +++ b/src/lib/routes/admin-api/project/health-report.ts @@ -5,7 +5,7 @@ import { IUnleashConfig } from '../../../types/option'; import ProjectHealthService from '../../../services/project-health-service'; import { Logger } from '../../../logger'; import { IArchivedQuery, IProjectParam } from '../../../types/model'; -import { handleErrors } from '../util'; +import { handleErrors } from '../../util'; export default class ProjectHealthReport extends Controller { private projectHealthService: ProjectHealthService; diff --git a/src/lib/routes/admin-api/state.ts b/src/lib/routes/admin-api/state.ts index c9046c724a..292d4cafdc 100644 --- a/src/lib/routes/admin-api/state.ts +++ b/src/lib/routes/admin-api/state.ts @@ -6,7 +6,6 @@ import { Request, Response } from 'express'; import Controller from '../controller'; import { ADMIN } from '../../types/permissions'; import extractUser from '../../extract-user'; -import { handleErrors } from './util'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; import { Logger } from '../../logger'; @@ -43,32 +42,28 @@ class StateController extends Controller { const userName = extractUser(req); const { drop, keep } = req.query; // TODO: Should override request type so file is a type on request - try { - let data; + let data; + // @ts-ignore + if (req.file) { // @ts-ignore - if (req.file) { + if (mime.getType(req.file.originalname) === 'text/yaml') { // @ts-ignore - if (mime.getType(req.file.originalname) === 'text/yaml') { - // @ts-ignore - data = YAML.safeLoad(req.file.buffer); - } else { - // @ts-ignore - data = JSON.parse(req.file.buffer); - } + data = YAML.safeLoad(req.file.buffer); } else { - data = req.body; + // @ts-ignore + data = JSON.parse(req.file.buffer); } - - await this.stateService.import({ - data, - userName, - dropBeforeImport: paramToBool(drop, false), - keepExisting: paramToBool(keep, true), - }); - res.sendStatus(202); - } catch (err) { - handleErrors(res, this.logger, err); + } else { + data = req.body; } + + await this.stateService.import({ + data, + userName, + dropBeforeImport: paramToBool(drop, false), + keepExisting: paramToBool(keep, true), + }); + res.sendStatus(202); } async export(req: Request, res: Response): Promise { @@ -84,30 +79,24 @@ class StateController extends Controller { const includeTags = paramToBool(req.query.tags, true); const includeEnvironments = paramToBool(req.query.environments, true); - try { - const data = await this.stateService.export({ - includeStrategies, - includeFeatureToggles, - includeProjects, - includeTags, - includeEnvironments, - }); - const timestamp = moment().format('YYYY-MM-DD_HH-mm-ss'); - if (format === 'yaml') { - if (downloadFile) { - res.attachment(`export-${timestamp}.yml`); - } - res.type('yaml').send( - YAML.safeDump(data, { skipInvalid: true }), - ); - } else { - if (downloadFile) { - res.attachment(`export-${timestamp}.json`); - } - res.json(data); + const data = await this.stateService.export({ + includeStrategies, + includeFeatureToggles, + includeProjects, + includeTags, + includeEnvironments, + }); + const timestamp = moment().format('YYYY-MM-DD_HH-mm-ss'); + if (format === 'yaml') { + if (downloadFile) { + res.attachment(`export-${timestamp}.yml`); } - } catch (err) { - handleErrors(res, this.logger, err); + res.type('yaml').send(YAML.safeDump(data, { skipInvalid: true })); + } else { + if (downloadFile) { + res.attachment(`export-${timestamp}.json`); + } + res.json(data); } } } diff --git a/src/lib/routes/admin-api/strategy.ts b/src/lib/routes/admin-api/strategy.ts index a97637d86b..c2bde58e5f 100644 --- a/src/lib/routes/admin-api/strategy.ts +++ b/src/lib/routes/admin-api/strategy.ts @@ -6,7 +6,7 @@ import { Logger } from '../../logger'; const Controller = require('../controller'); const extractUser = require('../../extract-user'); -const { handleErrors } = require('./util'); +const { handleErrors } = require('../util'); const { DELETE_STRATEGY, CREATE_STRATEGY, diff --git a/src/lib/routes/admin-api/tag-type.ts b/src/lib/routes/admin-api/tag-type.ts index 0d4b10c182..dcdd561369 100644 --- a/src/lib/routes/admin-api/tag-type.ts +++ b/src/lib/routes/admin-api/tag-type.ts @@ -2,7 +2,7 @@ import { Request, Response } from 'express'; import Controller from '../controller'; import { UPDATE_FEATURE } from '../../types/permissions'; -import { handleErrors } from './util'; +import { handleErrors } from '../util'; import extractUsername from '../../extract-user'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; diff --git a/src/lib/routes/admin-api/tag.ts b/src/lib/routes/admin-api/tag.ts index ceff7134cb..e8f3f3d334 100644 --- a/src/lib/routes/admin-api/tag.ts +++ b/src/lib/routes/admin-api/tag.ts @@ -7,7 +7,6 @@ import { Logger } from '../../logger'; import Controller from '../controller'; import { UPDATE_FEATURE } from '../../types/permissions'; -import { handleErrors } from './util'; import extractUsername from '../../extract-user'; const version = 1; @@ -33,52 +32,32 @@ class TagController extends Controller { } async getTags(req: Request, res: Response): Promise { - try { - const tags = await this.tagService.getTags(); - res.json({ version, tags }); - } catch (e) { - handleErrors(res, this.logger, e); - } + const tags = await this.tagService.getTags(); + res.json({ version, tags }); } async getTagsByType(req: Request, res: Response): Promise { - try { - const tags = await this.tagService.getTagsByType(req.params.type); - res.json({ version, tags }); - } catch (e) { - handleErrors(res, this.logger, e); - } + const tags = await this.tagService.getTagsByType(req.params.type); + res.json({ version, tags }); } async getTag(req: Request, res: Response): Promise { const { type, value } = req.params; - try { - const tag = await this.tagService.getTag({ type, value }); - res.json({ version, tag }); - } catch (err) { - handleErrors(res, this.logger, err); - } + const tag = await this.tagService.getTag({ type, value }); + res.json({ version, tag }); } async createTag(req: Request, res: Response): Promise { const userName = extractUsername(req); - try { - await this.tagService.createTag(req.body, userName); - res.status(201).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.tagService.createTag(req.body, userName); + res.status(201).end(); } async deleteTag(req: Request, res: Response): Promise { const { type, value } = req.params; const userName = extractUsername(req); - try { - await this.tagService.deleteTag({ type, value }, userName); - res.status(200).end(); - } catch (error) { - handleErrors(res, this.logger, error); - } + await this.tagService.deleteTag({ type, value }, userName); + res.status(200).end(); } } export default TagController; diff --git a/src/lib/routes/admin-api/user-admin.ts b/src/lib/routes/admin-api/user-admin.ts index 816b90a21c..cc2218fd7a 100644 --- a/src/lib/routes/admin-api/user-admin.ts +++ b/src/lib/routes/admin-api/user-admin.ts @@ -4,7 +4,7 @@ import { ADMIN } from '../../types/permissions'; import UserService from '../../services/user-service'; import { AccessService } from '../../services/access-service'; import { Logger } from '../../logger'; -import { handleErrors } from './util'; +import { handleErrors } from '../util'; import { IUnleashConfig } from '../../types/option'; import { EmailService } from '../../services/email-service'; import ResetTokenService from '../../services/reset-token-service'; diff --git a/src/lib/routes/admin-api/user-feedback-controller.ts b/src/lib/routes/admin-api/user-feedback-controller.ts index 322c4227c1..d8c21e04c1 100644 --- a/src/lib/routes/admin-api/user-feedback-controller.ts +++ b/src/lib/routes/admin-api/user-feedback-controller.ts @@ -2,11 +2,10 @@ import { Response } from 'express'; import Controller from '../controller'; import { Logger } from '../../logger'; -import { IUserRequest } from './user'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; import UserFeedbackService from '../../services/user-feedback-service'; -import { handleErrors } from './util'; +import { IAuthRequest } from '../unleash-types'; interface IFeedbackBody { neverShow?: boolean; @@ -32,7 +31,7 @@ class UserFeedbackController extends Controller { } private async recordFeedback( - req: IUserRequest, + req: IAuthRequest, res: Response, ): Promise { const BAD_REQUEST = 400; @@ -54,18 +53,12 @@ class UserFeedbackController extends Controller { neverShow: req.body.neverShow || false, }; - try { - const updated = await this.userFeedbackService.updateFeedback( - feedback, - ); - res.json(updated); - } catch (e) { - handleErrors(res, this.logger, e); - } + const updated = await this.userFeedbackService.updateFeedback(feedback); + res.json(updated); } private async updateFeedbackSettings( - req: IUserRequest, + req: IAuthRequest, res: Response, ): Promise { const { user } = req; @@ -78,14 +71,8 @@ class UserFeedbackController extends Controller { neverShow: req.body.neverShow || false, }; - try { - const updated = await this.userFeedbackService.updateFeedback( - feedback, - ); - res.json(updated); - } catch (e) { - handleErrors(res, this.logger, e); - } + const updated = await this.userFeedbackService.updateFeedback(feedback); + res.json(updated); } } diff --git a/src/lib/routes/admin-api/user.ts b/src/lib/routes/admin-api/user.ts index 5dcbf9c7a2..6b5299311f 100644 --- a/src/lib/routes/admin-api/user.ts +++ b/src/lib/routes/admin-api/user.ts @@ -1,13 +1,10 @@ -import { Request, Response } from 'express'; +import { Response } from 'express'; import { IAuthRequest } from '../unleash-types'; import Controller from '../controller'; import { AccessService } from '../../services/access-service'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; 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 UserFeedbackService from '../../services/user-feedback-service'; @@ -16,11 +13,6 @@ interface IChangeUserRequest { confirmPassword: string; } -export interface IUserRequest - extends Request { - user: User; -} - class UserController extends Controller { private accessService: AccessService; @@ -30,8 +22,6 @@ class UserController extends Controller { private sessionService: SessionService; - private logger: Logger; - constructor( config: IUnleashConfig, { @@ -52,7 +42,6 @@ class UserController extends Controller { this.userService = userService; this.sessionService = sessionService; this.userFeedbackService = userFeedbackService; - this.logger = config.getLogger('lib/routes/admin-api/user.ts'); this.get('/', this.getUser); this.post('/change-password', this.updateUserPass); @@ -76,34 +65,24 @@ class UserController extends Controller { } async updateUserPass( - req: IUserRequest, + req: IAuthRequest, res: Response, ): Promise { const { user } = req; const { password, confirmPassword } = req.body; - try { - if (password === confirmPassword) { - this.userService.validatePassword(password); - await this.userService.changePassword(user.id, password); - res.status(200).end(); - } else { - res.status(400).end(); - } - } catch (e) { - handleErrors(res, this.logger, e); + if (password === confirmPassword) { + this.userService.validatePassword(password); + await this.userService.changePassword(user.id, password); + res.status(200).end(); + } else { + res.status(400).end(); } } async mySessions(req: IAuthRequest, res: Response): Promise { const { user } = req; - try { - const sessions = await this.sessionService.getSessionsForUser( - user.id, - ); - res.json(sessions); - } catch (e) { - handleErrors(res, this.logger, e); - } + const sessions = await this.sessionService.getSessionsForUser(user.id); + res.json(sessions); } } diff --git a/src/lib/routes/auth/reset-password-controller.ts b/src/lib/routes/auth/reset-password-controller.ts index 07604bba19..5445cb0883 100644 --- a/src/lib/routes/auth/reset-password-controller.ts +++ b/src/lib/routes/auth/reset-password-controller.ts @@ -2,7 +2,6 @@ import { Request, Response } from 'express'; import Controller from '../controller'; import UserService from '../../services/user-service'; import { Logger } from '../../logger'; -import { handleErrors } from '../admin-api/util'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; @@ -40,23 +39,15 @@ class ResetPasswordController extends Controller { async sendResetPasswordEmail(req: Request, res: Response): Promise { const { email } = req.body; - try { - await this.userService.createResetPasswordEmail(email); - res.status(200).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + await this.userService.createResetPasswordEmail(email); + res.status(200).end(); } async validatePassword(req: Request, res: Response): Promise { const { password } = req.body; - try { - this.userService.validatePassword(password); - res.status(200).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + this.userService.validatePassword(password); + res.status(200).end(); } async validateToken( @@ -64,13 +55,9 @@ class ResetPasswordController extends Controller { res: Response, ): Promise { const { token } = req.query; - try { - const user = await this.userService.getUserForToken(token); - await this.logout(req); - res.status(200).json(user); - } catch (e) { - handleErrors(res, this.logger, e); - } + const user = await this.userService.getUserForToken(token); + await this.logout(req); + res.status(200).json(user); } async changePassword( @@ -79,12 +66,8 @@ class ResetPasswordController extends Controller { ): Promise { await this.logout(req); const { token, password } = req.body; - try { - await this.userService.resetPassword(token, password); - res.status(200).end(); - } catch (e) { - handleErrors(res, this.logger, e); - } + await this.userService.resetPassword(token, password); + res.status(200).end(); } private async logout(req: SessionRequest) { diff --git a/src/lib/routes/auth/simple-password-provider.js b/src/lib/routes/auth/simple-password-provider.js index eeda498ccb..fda12f788e 100644 --- a/src/lib/routes/auth/simple-password-provider.js +++ b/src/lib/routes/auth/simple-password-provider.js @@ -1,9 +1,9 @@ const Controller = require('../controller'); class PasswordProvider extends Controller { - constructor({ getLogger }, { userService }) { - super(); - this.logger = getLogger('/auth/password-provider.js'); + constructor(config, { userService }) { + super(config); + this.logger = config.getLogger('/auth/password-provider.js'); this.userService = userService; this.post('/login', this.login); diff --git a/src/lib/routes/client-api/feature.ts b/src/lib/routes/client-api/feature.ts index f7f239944e..676d380674 100644 --- a/src/lib/routes/client-api/feature.ts +++ b/src/lib/routes/client-api/feature.ts @@ -1,6 +1,6 @@ import memoizee from 'memoizee'; import { Request, Response } from 'express'; -import { handleErrors } from '../admin-api/util'; +import { handleErrors } from '../util'; import Controller from '../controller'; import { IUnleashServices } from '../../types/services'; import { IUnleashConfig } from '../../types/option'; diff --git a/src/lib/routes/controller.ts b/src/lib/routes/controller.ts index cbb42a33c4..87406f5012 100644 --- a/src/lib/routes/controller.ts +++ b/src/lib/routes/controller.ts @@ -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 { handleErrors } from './util'; const { Router } = require('express'); const NoAccessError = require('../error/no-access-error'); const requireContentType = require('../middleware/content_type_checker'); +interface IRequestHandler< + P = any, + ResBody = any, + ReqBody = any, + ReqQuery = any, +> { + ( + req: Request, + res: Response, + ): Promise | void; +} + const checkPermission = (permission) => async (req, res, next) => { if (!permission) { return next(); @@ -17,24 +31,48 @@ const checkPermission = (permission) => async (req, res, next) => { /** * 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 { + private ownLogger: Logger; + app: IRouter; config: IUnleashConfig; constructor(config: IUnleashConfig) { + this.ownLogger = config.getLogger( + `controller/${this.constructor.name}`, + ); this.app = Router(); this.config = config; } - get(path: string, handler: Function, permission?: string): void { - this.app.get(path, checkPermission(permission), handler.bind(this)); + wrap(handler: IRequestHandler): IRequestHandler { + 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( path: string, - handler: Function, + handler: IRequestHandler, permission?: string, ...acceptedContentTypes: string[] ): void { @@ -42,13 +80,13 @@ export default class Controller { path, checkPermission(permission), requireContentType(...acceptedContentTypes), - handler.bind(this), + this.wrap(handler.bind(this)), ); } put( path: string, - handler: Function, + handler: IRequestHandler, permission?: string, ...acceptedContentTypes: string[] ): void { @@ -56,17 +94,21 @@ export default class Controller { path, checkPermission(permission), requireContentType(...acceptedContentTypes), - handler.bind(this), + this.wrap(handler.bind(this)), ); } - delete(path: string, handler: Function, permission?: string): void { - this.app.delete(path, checkPermission(permission), handler.bind(this)); + delete(path: string, handler: IRequestHandler, permission?: string): void { + this.app.delete( + path, + checkPermission(permission), + this.wrap(handler.bind(this)), + ); } fileupload( path: string, - filehandler: Function, + filehandler: IRequestHandler, handler: Function, permission?: string, ): void { @@ -74,11 +116,10 @@ export default class Controller { path, checkPermission(permission), 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 { this.app.use(path, router); } diff --git a/src/lib/routes/health-check.ts b/src/lib/routes/health-check.ts index d8146d9358..0d65f470f3 100644 --- a/src/lib/routes/health-check.ts +++ b/src/lib/routes/health-check.ts @@ -15,7 +15,7 @@ class HealthCheckController extends Controller { config: IUnleashConfig, { healthService }: Pick, ) { - super(); + super(config); this.logger = config.getLogger('health-check.js'); this.healthService = healthService; this.get('/', (req, res) => this.index(req, res)); diff --git a/src/lib/routes/unleash-types.ts b/src/lib/routes/unleash-types.ts index 2362110d1f..45e8ce037d 100644 --- a/src/lib/routes/unleash-types.ts +++ b/src/lib/routes/unleash-types.ts @@ -1,7 +1,13 @@ import { Request } from 'express'; +import * as core from 'express-serve-static-core'; 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 { user: User; logout: () => void; session: any; diff --git a/src/lib/routes/admin-api/util.ts b/src/lib/routes/util.ts similarity index 98% rename from src/lib/routes/admin-api/util.ts rename to src/lib/routes/util.ts index df68845815..3d4164214b 100644 --- a/src/lib/routes/admin-api/util.ts +++ b/src/lib/routes/util.ts @@ -1,6 +1,6 @@ import joi from 'joi'; import { Response } from 'express'; -import { Logger } from '../../logger'; +import { Logger } from '../logger'; export const customJoi = joi.extend((j) => ({ type: 'isUrlFriendly', diff --git a/src/lib/schema/feature-schema.ts b/src/lib/schema/feature-schema.ts index dcf4787b96..8a1b14cd54 100644 --- a/src/lib/schema/feature-schema.ts +++ b/src/lib/schema/feature-schema.ts @@ -1,5 +1,5 @@ import joi from 'joi'; -import { nameType } from '../routes/admin-api/util'; +import { nameType } from '../routes/util'; export const nameSchema = joi .object() diff --git a/src/lib/server-impl.ts b/src/lib/server-impl.ts index 77f3f0000e..aa5e7dedce 100644 --- a/src/lib/server-impl.ts +++ b/src/lib/server-impl.ts @@ -14,6 +14,7 @@ import User from './types/user'; import * as permissions from './types/permissions'; import AuthenticationRequired from './types/authentication-required'; +import Controller from './routes/controller'; import * as eventType from './types/events'; import { addEventHook } from './event-hook'; import registerGracefulShutdown from './util/graceful-shutdown'; @@ -145,6 +146,7 @@ const serverImpl = { create, User, AuthenticationRequired, + Controller, permissions, eventType, }; diff --git a/src/lib/services/addon-schema.ts b/src/lib/services/addon-schema.ts index d642eeeb23..d653970092 100644 --- a/src/lib/services/addon-schema.ts +++ b/src/lib/services/addon-schema.ts @@ -1,5 +1,5 @@ import joi from 'joi'; -import { nameType } from '../routes/admin-api/util'; +import { nameType } from '../routes/util'; export const addonSchema = joi .object() diff --git a/src/lib/services/context-schema.js b/src/lib/services/context-schema.js index 161ee36ccf..6922709deb 100644 --- a/src/lib/services/context-schema.js +++ b/src/lib/services/context-schema.js @@ -1,7 +1,7 @@ 'use strict'; const joi = require('joi'); -const { nameType } = require('../routes/admin-api/util'); +const { nameType } = require('../routes/util'); const nameSchema = joi.object().keys({ name: nameType }); diff --git a/src/lib/services/project-schema.js b/src/lib/services/project-schema.js index d1ba202eca..d7acc9e610 100644 --- a/src/lib/services/project-schema.js +++ b/src/lib/services/project-schema.js @@ -1,5 +1,5 @@ const joi = require('joi'); -const { nameType } = require('../routes/admin-api/util'); +const { nameType } = require('../routes/util'); const projectSchema = joi .object() diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 334938c101..5d4626c57b 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -2,7 +2,7 @@ import User from '../types/user'; import { AccessService } from './access-service'; import NameExistsError from '../error/name-exists-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 NotFoundError from '../error/notfound-error'; import { diff --git a/src/lib/services/state-schema.ts b/src/lib/services/state-schema.ts index 4ca6b018c2..51bdeae7bb 100644 --- a/src/lib/services/state-schema.ts +++ b/src/lib/services/state-schema.ts @@ -4,7 +4,7 @@ import strategySchema from './strategy-schema'; import { tagSchema } from './tag-schema'; import { tagTypeSchema } from './tag-type-schema'; import projectSchema from './project-schema'; -import { nameType } from '../routes/admin-api/util'; +import { nameType } from '../routes/util'; export const featureStrategySchema = joi .object() diff --git a/src/lib/services/strategy-schema.ts b/src/lib/services/strategy-schema.ts index 6e24cc8e4a..ea4e55af32 100644 --- a/src/lib/services/strategy-schema.ts +++ b/src/lib/services/strategy-schema.ts @@ -1,5 +1,5 @@ const joi = require('joi'); -const { nameType } = require('../routes/admin-api/util'); +const { nameType } = require('../routes/util'); const strategySchema = joi .object() diff --git a/src/lib/services/tag-schema.ts b/src/lib/services/tag-schema.ts index 2dc20291f5..78cff4ba45 100644 --- a/src/lib/services/tag-schema.ts +++ b/src/lib/services/tag-schema.ts @@ -1,6 +1,6 @@ import Joi from 'joi'; -import { customJoi } from '../routes/admin-api/util'; +import { customJoi } from '../routes/util'; export const tagSchema = Joi.object() .keys({ diff --git a/src/lib/services/tag-type-schema.ts b/src/lib/services/tag-type-schema.ts index ec212fcf80..1fecdd4b63 100644 --- a/src/lib/services/tag-type-schema.ts +++ b/src/lib/services/tag-type-schema.ts @@ -1,5 +1,5 @@ import Joi from 'joi'; -import { customJoi } from '../routes/admin-api/util'; +import { customJoi } from '../routes/util'; export const tagTypeSchema = Joi.object() .keys({