Merge pull request #4 from Permissionless-Software-Foundation/ct-unstable

feat(utxos): Added utxos endpoing to REST and JSON RPC
This commit is contained in:
Chris Troutner
2021-07-15 19:07:11 -07:00
committed by GitHub
10 changed files with 276 additions and 76 deletions
@@ -11,19 +11,19 @@ const BCHJS = require('@psf/bch-js')
const Validators = require('../validators')
const RateLimit = require('../rate-limit')
class FulcrumRPC {
class BCHRPC {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Fulcrum JSON RPC Controller.'
'Instance of Adapters library required when instantiating BCH JSON RPC Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Fulcrum JSON RPC Controller.'
'Instance of Use Cases library required when instantiating BCH JSON RPC Controller.'
)
}
@@ -38,7 +38,7 @@ class FulcrumRPC {
// Top-level router for this library. All other methods in this class are for
// a specific endpoint. This method routes incoming calls to one of those
// methods.
async fulcrumRouter (rpcData) {
async bchRouter (rpcData) {
let endpoint = 'unknown'
try {
// console.log('fulcrumRouter rpcData: ', rpcData)
@@ -52,10 +52,13 @@ class FulcrumRPC {
await this.rateLimit.limiter(rpcData.from)
return await this.transactions(rpcData)
// case 'getAllUsers':
// await this.validators.ensureUser(rpcData)
// await this.rateLimit.limiter(rpcData.from)
// return await this.getAll(rpcData)
case 'balance':
await this.rateLimit.limiter(rpcData.from)
return await this.balance(rpcData)
case 'utxos':
await this.rateLimit.limiter(rpcData.from)
return await this.utxos(rpcData)
//
// case 'getUser':
// user = await this.validators.ensureUser(rpcData)
@@ -73,7 +76,7 @@ class FulcrumRPC {
// return await this.deleteUser(rpcData, user)
}
} catch (err) {
console.error('Error in FulcrumRPC/rpcRouter()')
console.error('Error in BCHRPC/rpcRouter()')
// throw err
return {
@@ -86,14 +89,14 @@ class FulcrumRPC {
}
/**
* @api {JSON} /fulcrum Transactions
* @api {JSON} /bch Transactions
* @apiPermission public
* @apiName Transactions
* @apiGroup JSON Fulcrum
* @apiGroup JSON BCH
* @apiDescription This endpoint wraps the bchjs.Electrumx.transactions([]) function.
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"fulcrum","params":{ "endpoint": "transactions", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"]}}
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "transactions", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"]}}
*
*/
async transactions (rpcData) {
@@ -124,14 +127,14 @@ class FulcrumRPC {
}
/**
* @api {JSON} /fulcrum Balance
* @api {JSON} /bch Balance
* @apiPermission public
* @apiName Balance
* @apiGroup JSON Fulcrum
* @apiGroup JSON BCH
* @apiDescription This endpoint wraps the bchjs.Electrumx.balance([]) function.
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"fulcrum","params":{ "endpoint": "balance", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"]}}
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "balance", "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"]}}
*
*/
async balance (rpcData) {
@@ -161,7 +164,47 @@ class FulcrumRPC {
}
}
/**
* @api {JSON} /bch UTXOs
* @apiPermission public
* @apiName UTXOs
* @apiGroup JSON BCH
* @apiDescription This endpoint wraps the bchjs.Utxos.get() function. This
* endpoint returns UTXOs held at an address, hydrated
* with token information.
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "utxos", "address": "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"}}
*
*/
async utxos (rpcData) {
try {
// console.log('createUser rpcData: ', rpcData)
const addr = rpcData.payload.params.address
const data = await this.bchjs.Utxo.get(addr)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
const retObj = data
retObj.status = 200
return retObj
} catch (err) {
// console.error('Error in createUser()')
// throw err
// Return an error response
return {
success: false,
status: 422,
message: err.message,
endpoint: 'utxos'
}
}
}
// TODO create deleteUser()
}
module.exports = FulcrumRPC
module.exports = BCHRPC
+4 -4
View File
@@ -10,7 +10,7 @@ const { wlogger } = require('../../adapters/wlogger')
const UserController = require('./users')
const AuthController = require('./auth')
const AboutController = require('./about')
const FulcrumController = require('./fulcrum')
const BCHController = require('./bch')
let _this
@@ -36,7 +36,7 @@ class JSONRPC {
this.userController = new UserController(localConfig)
this.authController = new AuthController(localConfig)
this.aboutController = new AboutController()
this.fulcrumController = new FulcrumController(localConfig)
this.bchController = new BCHController(localConfig)
_this = this
}
@@ -83,8 +83,8 @@ class JSONRPC {
case 'about':
retObj = await _this.aboutController.aboutRouter(parsedData)
break
case 'fulcrum':
retObj = await _this.fulcrumController.fulcrumRouter(parsedData)
case 'bch':
retObj = await _this.bchController.bchRouter(parsedData)
}
// console.log('retObj: ', retObj)
@@ -6,19 +6,19 @@ const BCHJS = require('@psf/bch-js')
let _this
class FulcrumRESTController {
class BCHRESTController {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Fulcrum REST Controller.'
'Instance of Adapters library required when instantiating BCH REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Fulcrum REST Controller.'
'Instance of Use Cases library required when instantiating BCH REST Controller.'
)
}
@@ -28,13 +28,13 @@ class FulcrumRESTController {
}
/**
* @api {post} /fulcrum/transactions Transactions
* @api {post} /bch/transactions Transactions
* @apiName Transactions
* @apiGroup REST Fulcrum
* @apiGroup REST BCH
* @apiDescription This endpoint wraps the bchjs.Electrumx.transactions([]) function.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"] }' localhost:5001/fulcrum/transactions
* curl -H "Content-Type: application/json" -X POST -d '{ "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"] }' localhost:5001/bch/transactions
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
@@ -67,13 +67,13 @@ class FulcrumRESTController {
}
/**
* @api {post} /fulcrum/balance Balance
* @api {post} /bch/balance Balance
* @apiName Balance
* @apiGroup REST Fulcrum
* @apiGroup REST BCH
* @apiDescription This endpoint returns the balance in BCH for an address.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"] }' localhost:5001/fulcrum/balance
* curl -H "Content-Type: application/json" -X POST -d '{ "addresses": ["bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj"] }' localhost:5001/bch/balance
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
@@ -104,6 +104,45 @@ class FulcrumRESTController {
}
}
/**
* @api {post} /bch/utxos Balance
* @apiName Utxos
* @apiGroup REST BCH
* @apiDescription This endpoint returns UTXOs held at an address, hydrated
* with token information.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "address": "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj" }' localhost:5001/bch/utxos
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* success:true,
* data: <data>
* }
*
* @apiError UnprocessableEntity Missing required parameters
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 422 Unprocessable Entity
* {
* "status": 422,
* "error": "Unprocessable Entity"
* }
*/
async utxos (ctx) {
try {
const address = ctx.request.body.address
const utxos = await _this.bchjs.Utxo.get(address)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
ctx.body = utxos
} catch (err) {
_this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
@@ -119,4 +158,4 @@ class FulcrumRESTController {
}
}
}
module.exports = FulcrumRESTController
module.exports = BCHRESTController
@@ -6,21 +6,21 @@
const Router = require('koa-router')
// Local libraries.
const FulcrumRESTController = require('./controller')
const BCHRESTController = require('./controller')
class FulcrumRouter {
class BCHRouter {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Fulcrum REST Controller.'
'Instance of Adapters library required when instantiating BCH REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Fulcrum REST Controller.'
'Instance of Use Cases library required when instantiating BCH REST Controller.'
)
}
@@ -30,10 +30,10 @@ class FulcrumRouter {
}
// Encapsulate dependencies.
this.fulcrumRESTController = new FulcrumRESTController(dependencies)
this.bchRESTController = new BCHRESTController(dependencies)
// Instantiate the router and set the base route.
const baseUrl = '/fulcrum'
const baseUrl = '/bch'
this.router = new Router({ prefix: baseUrl })
}
@@ -45,7 +45,9 @@ class FulcrumRouter {
}
// Define the routes and attach the controller.
this.router.post('/transactions', this.fulcrumRESTController.transactions)
this.router.post('/transactions', this.bchRESTController.transactions)
this.router.post('/balance', this.bchRESTController.balance)
this.router.post('/utxos', this.bchRESTController.utxos)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
@@ -53,4 +55,4 @@ class FulcrumRouter {
}
}
module.exports = FulcrumRouter
module.exports = BCHRouter
+3 -3
View File
@@ -11,7 +11,7 @@ const AuthRESTController = require('./auth')
const UserRouter = require('./users')
const ContactRESTController = require('./contact')
const LogsRESTController = require('./logs')
const FulcrumRESTController = require('./fulcrum')
const BCHRESTController = require('./bch')
class RESTControllers {
constructor (localConfig = {}) {
@@ -55,8 +55,8 @@ class RESTControllers {
logsRESTController.attach(app)
// Attach the REST API Controllers associated with the /fulcrum route
const fulcrumRESTController = new FulcrumRESTController(dependencies)
fulcrumRESTController.attach(app)
const bchRESTController = new BCHRESTController(dependencies)
bchRESTController.attach(app)
}
}
@@ -12,12 +12,12 @@ const { v4: uid } = require('uuid')
process.env.SVC_ENV = 'test'
// Local libraries
const FulcrumRPC = require('../../../../src/controllers/json-rpc/fulcrum')
const BCHRPC = require('../../../../src/controllers/json-rpc/bch')
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#FulcrumRPC', () => {
describe('#BCHRPC', () => {
let uut
let sandbox
@@ -26,7 +26,7 @@ describe('#FulcrumRPC', () => {
const useCases = new UseCasesMock()
uut = new FulcrumRPC({ adapters, useCases })
uut = new BCHRPC({ adapters, useCases })
uut.rateLimit = new RateLimit({ max: 100 })
})
@@ -35,32 +35,32 @@ describe('#FulcrumRPC', () => {
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new FulcrumRPC()
uut = new BCHRPC()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Fulcrum JSON RPC Controller.'
'Instance of Adapters library required when instantiating BCH JSON RPC Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new FulcrumRPC({ adapters })
uut = new BCHRPC({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Fulcrum JSON RPC Controller.'
'Instance of Use Cases library required when instantiating BCH JSON RPC Controller.'
)
}
})
})
describe('#fulcrumRouter', () => {
describe('#bchRouter', () => {
it('should route to the transactions method', async () => {
// Mock dependencies
sandbox.stub(uut, 'transactions').resolves(true)
@@ -68,14 +68,52 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'fulcrum', {
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.fulcrumRouter(rpcData)
const result = await uut.bchRouter(rpcData)
assert.equal(result, true)
})
it('should route to the balance method', async () => {
// Mock dependencies
sandbox.stub(uut, 'balance').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'balance'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.bchRouter(rpcData)
assert.equal(result, true)
})
it('should route to the utxos method', async () => {
// Mock dependencies
sandbox.stub(uut, 'utxos').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'utxos'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.bchRouter(rpcData)
assert.equal(result, true)
})
@@ -87,14 +125,14 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'fulcrum', {
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions'
})
const jsonStr = JSON.stringify(txCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.fulcrumRouter(rpcData)
const result = await uut.bchRouter(rpcData)
assert.equal(result.success, false)
assert.equal(result.status, 500)
@@ -113,7 +151,7 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'fulcrum', {
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions',
addresses: 'testAddr'
})
@@ -136,7 +174,7 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const txCall = jsonrpc.request(id, 'fulcrum', {
const txCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions',
addresses: 'testAddr'
})
@@ -161,7 +199,7 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'fulcrum', {
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'balance',
addresses: 'testAddr'
})
@@ -184,7 +222,7 @@ describe('#FulcrumRPC', () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'fulcrum', {
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'balance',
addresses: 'testAddr'
})
@@ -200,4 +238,50 @@ describe('#FulcrumRPC', () => {
assert.equal(response.endpoint, 'balance')
})
})
describe('#utxos', () => {
it('should return data from bchjs', async () => {
// Mock dependencies
sandbox.stub(uut.bchjs.Utxo, 'get').resolves({ success: true })
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'utxos',
addresses: 'testAddr'
})
const jsonStr = JSON.stringify(rpcCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.utxos(rpcData)
// console.log('response: ', response)
assert.equal(response.success, true)
assert.equal(response.status, 200)
})
it('should return an error for invalid address', async () => {
// Force an error
sandbox.stub(uut.bchjs.Utxo, 'get').rejects(new Error('Invalid address'))
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const rpcCall = jsonrpc.request(id, 'bch', {
endpoint: 'utxos',
addresses: 'testAddr'
})
const jsonStr = JSON.stringify(rpcCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.utxos(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'Invalid address')
assert.equal(response.endpoint, 'utxos')
})
})
})
@@ -180,15 +180,15 @@ describe('#JSON RPC', () => {
assert.equal(obj.id, id)
})
it('should route to fulcrum handler', async () => {
it('should route to bch handler', async () => {
const id = uid()
const userCall = jsonrpc.request(id, 'fulcrum', {
const userCall = jsonrpc.request(id, 'bch', {
endpoint: 'transactions'
})
const jsonStr = JSON.stringify(userCall, null, 2)
// Mock the controller.
sandbox.stub(uut.fulcrumController, 'fulcrumRouter').resolves('true')
sandbox.stub(uut.bchController, 'bchRouter').resolves('true')
// Force ipfs-coord communication.
uut.ipfsCoord.ipfs = {
@@ -204,7 +204,7 @@ describe('#JSON RPC', () => {
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.result.method, 'fulcrum')
assert.equal(obj.result.method, 'bch')
assert.equal(obj.id, id)
})
})
@@ -11,19 +11,19 @@ const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const FulcrumRESTController = require('../../../../../src/controllers/rest-api/fulcrum/controller')
const BCHRESTController = require('../../../../../src/controllers/rest-api/bch/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Fulcrum-REST-Router', () => {
describe('#BCH-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new FulcrumRESTController({ adapters, useCases })
uut = new BCHRESTController({ adapters, useCases })
sandbox = sinon.createSandbox()
@@ -36,26 +36,26 @@ describe('#Fulcrum-REST-Router', () => {
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new FulcrumRESTController()
uut = new BCHRESTController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Fulcrum REST Controller.'
'Instance of Adapters library required when instantiating BCH REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new FulcrumRESTController({ adapters })
uut = new BCHRESTController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Fulcrum REST Controller.'
'Instance of Use Cases library required when instantiating BCH REST Controller.'
)
}
})
@@ -131,6 +131,38 @@ describe('#Fulcrum-REST-Router', () => {
})
})
describe('#utxos', () => {
it('should return data from bchjs', async () => {
// Mock dependencies
sandbox.stub(uut.bchjs.Utxo, 'get').resolves({ success: true })
ctx.request.body = {
addresses: 'testAddr'
}
await uut.utxos(ctx)
// console.log('ctx.body: ', ctx.body)
assert.equal(ctx.body.success, true)
})
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox.stub(uut.bchjs.Utxo, 'get').rejects(new Error('test error'))
ctx.request.body = {
addresses: 'testAddr'
}
await uut.utxos(ctx)
} catch (err) {
// console.log('err: ', err)
assert.include(err.message, 'test error')
}
})
})
describe('#handleError', () => {
it('should pass an error message', () => {
try {
@@ -1,5 +1,5 @@
/*
Unit tests for the REST API handler for the /fulcrum endpoints.
Unit tests for the REST API handler for the /bch endpoints.
*/
// Public npm libraries
@@ -11,19 +11,19 @@ const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const FulcrumRouter = require('../../../../../src/controllers/rest-api/fulcrum')
const BCHRouter = require('../../../../../src/controllers/rest-api/bch')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Fulcrum-REST-Router', () => {
describe('#BCH-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new FulcrumRouter({ adapters, useCases })
uut = new BCHRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
@@ -36,26 +36,26 @@ describe('#Fulcrum-REST-Router', () => {
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new FulcrumRouter()
uut = new BCHRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Fulcrum REST Controller.'
'Instance of Adapters library required when instantiating BCH REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new FulcrumRouter({ adapters })
uut = new BCHRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Fulcrum REST Controller.'
'Instance of Use Cases library required when instantiating BCH REST Controller.'
)
}
})