feat(endpoint): Added controller to enable endpoint

This commit is contained in:
Daniel Gonzalez
2021-08-03 19:40:21 -04:00
parent d6bed90132
commit 7dfcb85ab9
7 changed files with 340 additions and 2 deletions
@@ -0,0 +1,66 @@
/*
REST API Controller library for the /entry route
*/
// const { wlogger } = require('../../../adapters/wlogger')
let _this
class EntryRESTControllerLib {
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.'
)
}
// Encapsulate dependencies
this.EntryModel = this.adapters.localdb.Entry
// this.userUseCases = this.useCases.user
_this = this
}
// No Documentation because this wont be a public endpoint
async createEntry (ctx) {
try {
const entryObj = ctx.request.body.entry
const entry = await _this.useCases.entry.createEntry(entryObj)
// console.log('userData: ', userData)
// console.log('token: ', token)
ctx.body = { entry }
} catch (err) {
// console.log(`err.message: ${err.message}`)
// console.log('err: ', err)
// ctx.throw(422, err.message)
_this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
console.log('err', err.message)
// If an HTTP status is specified by the buisiness logic, use that.
if (err.status) {
if (err.message) {
ctx.throw(err.status, err.message)
} else {
ctx.throw(err.status)
}
} else {
// By default use a 422 error if the HTTP status is not specified.
ctx.throw(422, err.message)
}
}
}
module.exports = EntryRESTControllerLib
+60
View File
@@ -0,0 +1,60 @@
/*
REST API library for /user route.
*/
// Public npm libraries.
const Router = require('koa-router')
// Local libraries.
const EntryRESTControllerLib = require('./controller')
let _this
class UserRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating PostEntry REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating PostEntry REST Controller.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
// Encapsulate dependencies.
this.entryRESTController = new EntryRESTControllerLib(dependencies)
// Instantiate the router and set the base route.
const baseUrl = '/entry'
this.router = new Router({ prefix: baseUrl })
_this = this
}
attach (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.entryRESTController.createEntry)
// Attach the Controller routes to the Koa app.
app.use(_this.router.routes())
app.use(_this.router.allowedMethods())
}
}
module.exports = UserRouter
+5
View File
@@ -11,6 +11,7 @@ const AuthRESTController = require('./auth')
const UserRouter = require('./users')
const ContactRESTController = require('./contact')
const LogsRESTController = require('./logs')
const EntryRouter = require('./entry')
class RESTControllers {
constructor (localConfig = {}) {
@@ -52,6 +53,10 @@ class RESTControllers {
// Attach the REST API Controllers associated with the /logs route
const logsRESTController = new LogsRESTController(dependencies)
logsRESTController.attach(app)
// Attach the REST API Controllers associated with the /entry route
const entryRouter = new EntryRouter(dependencies)
entryRouter.attach(app)
}
}
+2 -2
View File
@@ -24,7 +24,7 @@ class EntryLib {
const entryEntity = this.EntryEntity.validate(entryObj)
// Verify that the entry was signed by a specific BCH address.
const isValidSignature = this.bchjs._verifySignature(entryEntity.slpAddress)
const isValidSignature = this.bchjs._verifySignature(entryEntity)
if (!isValidSignature) {
throw new Error('Invalid signature')
}
@@ -33,7 +33,7 @@ class EntryLib {
const psfBalance = await this.bchjs.getPSFTokenBalance(entryEntity.slpAddress)
if (psfBalance < 10) {
if (psfBalance < 1) {
throw new Error('Insufficient psf balance')
}
@@ -0,0 +1,122 @@
/*
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 EntryController = require('../../../../../src/controllers/rest-api/entry/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Entry-REST-Controller', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new EntryController({ 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 EntryController()
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 EntryController({ 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 /entry', () => {
it('should return 422 status on biz logic error', async () => {
try {
await uut.createEntry(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 200 status on success', async () => {
ctx.request.body = {
entry: {
entry: 'entry',
description: 'test',
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
signature: 'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
category: 'test'
}
}
await uut.createEntry(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.property(ctx.response.body, 'entry')
})
})
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')
}
})
it('should catch error if message is provided', () => {
try {
const err = {
status: 422,
message: 'Unprocessable Entity'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Unprocessable Entity')
}
})
})
})
@@ -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 EntryRouter = require('../../../../../src/controllers/rest-api/entry')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Entry-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new EntryRouter({ 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 EntryRouter()
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 EntryRouter({ 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.'
)
}
})
})
})
+7
View File
@@ -31,12 +31,19 @@ class UserUseCaseMock {
}
}
class EntryUseCaseMock {
async createEntry(userObj) {
return {}
}
}
class UseCasesMock {
constuctor(localConfig = {}) {
// this.user = new UserUseCaseMock(localConfig)
}
user = new UserUseCaseMock()
entry = new EntryUseCaseMock()
}
module.exports = UseCasesMock