Refactored users REST API controller and router

This commit is contained in:
Chris Troutner
2021-07-12 17:37:49 -07:00
parent 49c3820898
commit 078b708a3e
10 changed files with 265 additions and 156 deletions
+6 -6
View File
@@ -8,23 +8,23 @@
// Load the REST API Controllers.
const AuthRESTController = require('./auth')
const UserRESTController = require('./users')
const UserRouter = require('./users')
const ContactRESTController = require('./contact')
const LogsRESTController = require('./logs')
class RESTControllers {
constructor (localConfig) {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating PostEntry REST Controller.'
'Instance of Adapters library required when instantiating REST Controller libraries.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating PostEntry REST Controller.'
'Instance of Use Cases library required when instantiating REST Controller libraries.'
)
}
@@ -42,8 +42,8 @@ class RESTControllers {
authRESTController.attach(app)
// Attach the REST API Controllers associated with the /user route
const userRESTController = new UserRESTController(dependencies)
userRESTController.attach(app)
const userRouter = new UserRouter(dependencies)
userRouter.attach(app)
// Attach the REST API Controllers associated with the /contact route
const contactRESTController = new ContactRESTController(dependencies)
@@ -2,12 +2,6 @@
REST API Controller library for the /user route
*/
// User database model.
// const UserModel = require('../../../adapters/localdb/models/users')
// User library for business logic.
// const UserLib = require('../../../use-cases/user')
const { wlogger } = require('../../../adapters/wlogger')
let _this
+62 -5
View File
@@ -2,9 +2,16 @@
REST API library for /user route.
*/
const UserRESTRouter = require('./router')
// Public npm libraries.
const Router = require('koa-router')
class UserRESTController {
// Local libraries.
const UserRESTControllerLib = require('./controller')
const Validators = require('../middleware/validators')
let _this
class UserRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
@@ -20,12 +27,62 @@ class UserRESTController {
)
}
this.userRESTRouter = new UserRESTRouter(localConfig)
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.userRESTController = new UserRESTControllerLib(dependencies)
this.validators = new Validators()
// Instantiate the router and set the base route.
const baseUrl = '/users'
this.router = new Router({ prefix: baseUrl })
_this = this
}
attach (app) {
this.userRESTRouter.attachControllers(app)
if (!app) {
throw new Error(
'Must pass app object when attaching REST API controllers.'
)
}
// Define the routes and attach the controller.
this.router.post('/', this.userRESTController.createUser)
this.router.get('/', this.getAll)
this.router.get('/:id', this.getById)
this.router.put('/:id', this.updateUser)
this.router.delete('/:id', this.deleteUser)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
}
async getAll (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.userRESTController.getUsers(ctx, next)
}
async getById (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.userRESTController.getUser(ctx, next)
}
async updateUser (ctx, next) {
await _this.validators.ensureTargetUserOrAdmin(ctx, next)
await _this.userRESTController.getUser(ctx, next)
await _this.userRESTController.updateUser(ctx, next)
}
async deleteUser (ctx, next) {
await _this.validators.ensureTargetUserOrAdmin(ctx, next)
await _this.userRESTController.getUser(ctx, next)
await _this.userRESTController.deleteUser(ctx, next)
}
}
module.exports = UserRESTController
module.exports = UserRouter
-87
View File
@@ -1,87 +0,0 @@
/*
REST Router for the /user route.
*/
// Public npm libraries.
const Router = require('koa-router')
// Local libraries.
const UserRESTControllerLib = require('./controller')
const Validators = require('../middleware/validators')
let _this
class UserRESTRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating /users REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating /users REST Controller.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.userRESTController = new UserRESTControllerLib(dependencies)
this.validators = new Validators()
// Instantiate the router and set the base route.
const baseUrl = '/users'
this.router = new Router({ prefix: baseUrl })
_this = this
}
attachControllers (app) {
if (!app) {
throw new Error(
'Must pass app object when attaching REST API controllers.'
)
}
// Define the routes and attach the controller.
this.router.post('/', this.userRESTController.createUser)
this.router.get('/', this.getAll)
this.router.get('/:id', this.getById)
this.router.put('/:id', this.updateUser)
this.router.delete('/:id', this.deleteUser)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
}
async getAll (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.userRESTController.getUsers(ctx, next)
}
async getById (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.userRESTController.getUser(ctx, next)
}
async updateUser (ctx, next) {
await _this.validators.ensureTargetUserOrAdmin(ctx, next)
await _this.userRESTController.getUser(ctx, next)
await _this.userRESTController.updateUser(ctx, next)
}
async deleteUser (ctx, next) {
await _this.validators.ensureTargetUserOrAdmin(ctx, next)
await _this.userRESTController.getUser(ctx, next)
await _this.userRESTController.deleteUser(ctx, next)
}
}
module.exports = UserRESTRouter
+1 -1
View File
@@ -292,7 +292,7 @@ describe('Users', () => {
assert.fail('Unexpected code path!')
} catch (err) {
console.log(err)
// console.log(err)
assert.equal(err.response.status, 422)
assert.equal(err.response.data, 'test error')
}
@@ -13,8 +13,6 @@ process.env.SVC_ENV = 'test'
// Local libraries
const UserRPC = require('../../../../src/controllers/json-rpc/users')
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
// const UserModel = require('../../../src/adapters/localdb/models/users')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
@@ -29,7 +27,6 @@ describe('#UserRPC', () => {
const useCases = new UseCasesMock()
uut = new UserRPC({ adapters, useCases })
uut.rateLimit = new RateLimit({ max: 100 })
})
afterEach(() => sandbox.restore())
@@ -0,0 +1,64 @@
/*
Unit tests for the REST API controllers/rest-api/index.js library.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const RESTControllers = require('../../../../src/controllers/rest-api/')
// const mockContext = require('../../../unit/mocks/ctx-mock').context
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#RESTControllers', () => {
let uut
let sandbox
// let ctx
before(async () => {})
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new RESTControllers({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
// ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new RESTControllers()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating REST Controller libraries.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new RESTControllers({ adapters })
assert.fail('Unexpected code path')
// use to prevent complaints from linter.
console.log('uut: ', uut)
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating REST Controller libraries.'
)
}
})
})
})
@@ -5,62 +5,21 @@
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
const mongoose = require('mongoose')
// Local support libraries
const config = require('../../../../config')
const testUtils = require('../../../utils/test-utils')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
const UserController = require('../../../../src/controllers/rest-api/users/controller')
const UserController = require('../../../../../src/controllers/rest-api/users/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../unit/mocks/ctx-mock').context
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Users', () => {
describe('#Users-REST-Controller', () => {
// const testUser = {}
before(async () => {
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
await mongoose.connect(config.database, {
useUnifiedTopology: true,
useNewUrlParser: true
})
// Delete all previous users in the database.
await testUtils.deleteAllUsers()
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
// Create a second test user.
// const userObj = {
// email: 'test2@test.com',
// password: 'pass2'
// }
// const testUser = await testUtils.createUser(userObj)
// console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`)
// context.user2 = testUser.user
// context.token2 = testUser.token
// context.id2 = testUser.user._id
// Get the JWT used to log in as the admin 'system' user.
// const adminJWT = await testUtils.getAdminJWT()
// // console.log(`adminJWT: ${adminJWT}`)
// context.adminJWT = adminJWT
// const admin = await testUtils.loginAdminUser()
// context.adminJWT = admin.token
// const admin = await adminLib.loginAdmin()
// console.log(`admin: ${JSON.stringify(admin, null, 2)}`)
})
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UserController({ adapters, useCases })
@@ -73,8 +32,32 @@ describe('Users', () => {
afterEach(() => sandbox.restore())
after(() => {
mongoose.connection.close()
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UserController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /users REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UserController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /users REST Controller.'
)
}
})
})
describe('#POST /users', () => {
@@ -258,4 +241,18 @@ describe('Users', () => {
assert.equal(ctx.status, 200)
})
})
describe('#handleError', () => {
it('should still throw error if there is no message', () => {
try {
const err = {
status: 404
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Not Found')
}
})
})
})
@@ -0,0 +1,78 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const UserRouter = require('../../../../../src/controllers/rest-api/users')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Users-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UserRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
// ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UserRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating PostEntry REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UserRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating PostEntry REST Controller.'
)
}
})
})
describe('#attach', () => {
it('should throw an error if app is not passed in.', () => {
try {
uut.attach()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Must pass app object when attaching REST API controllers.'
)
}
})
})
})
+9
View File
@@ -0,0 +1,9 @@
/*
Mocks for Koa 'app' object.
*/
const app = {
use: () => {}
}
module.exports = app