Skip to content

Commit 9429cd5

Browse files
f3rnoJacobPlaster
authored andcommitted
(refactor) improve docs, tweak internal structure
1 parent 09c886e commit 9429cd5

File tree

6 files changed

+413
-269
lines changed

6 files changed

+413
-269
lines changed

examples/endpoint_test.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ process.env.DEBUG = 'bfx:*'
44

55
const assert = require('assert')
66
const debug = require('debug')('bfx:api:mock-srv:examples:endpoint-test')
7-
const { MockRESTv2Server } = require('../')
87
const { RESTv2 } = require('bfx-api-node-rest')
8+
const { MockRESTv2Server } = require('../')
9+
10+
const FUNDING_OFFER = [
11+
41215275, 'fUSD', 1524784806000, 1524784806000, 1000, 1000, 'FRRDELTAVAR',
12+
null, null, 0, 'ACTIVE', null, null, null, 0, 30, 0, 0, null, 0, 0.00207328
13+
]
914

1015
debug('spawning mock server...')
1116

@@ -16,20 +21,17 @@ const rest = new RESTv2({
1621
url: 'http://localhost:9999'
1722
})
1823

19-
const fundingOffer = [
20-
41215275, 'fUSD', 1524784806000, 1524784806000, 1000, 1000, 'FRRDELTAVAR',
21-
null, null, 0, 'ACTIVE', null, null, null, 0, 30, 0, 0, null, 0, 0.00207328
22-
]
23-
24-
srv.setResponse('f_offers.fUSD', [fundingOffer])
24+
srv.setResponse('f_offers.fUSD', [FUNDING_OFFER])
2525

2626
debug('requesting preset response...')
2727

2828
rest.fundingOffers('fUSD').then(([incomingFundingOffer]) => {
29-
assert.deepStrictEqual(incomingFundingOffer, fundingOffer)
29+
assert.deepStrictEqual(incomingFundingOffer, FUNDING_OFFER)
3030

3131
debug('correct response received')
3232
srv.close()
33+
34+
return null
3335
}).catch((e) => {
3436
debug(`error: ${e.message}`)
3537
})

index.js

+50-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,53 @@
11
'use strict'
22

3-
const MockRESTv2Server = require('./lib/servers/rest2')
4-
const MockWSv2Server = require('./lib/servers/ws2')
3+
const MockServer = require('./lib/server')
4+
const MockWSv2Server = require('./lib/ws2')
5+
const MockRESTv2Server = require('./lib/rest2')
56

6-
module.exports = { MockRESTv2Server, MockWSv2Server }
7+
/**
8+
* This module hosts mock servers for the
9+
* {@link module:bfx-api-mock-srv.MockWSv2Server|WSv2} and
10+
* {@link module:bfx-api-mock-srv.MockRESTv2Server|RESTv2} Bitfinex APIs, and
11+
* is intended for testing the Bitfinex API libraries.
12+
*
13+
* @license Apache-2.0
14+
* @module bfx-api-mock-srv
15+
* @example
16+
* const { MockRESTv2Server } = require('bfx-api-mock-srv')
17+
*
18+
* const FUNDING_OFFER = [
19+
* 41215275, 'fUSD', 1524784806000, 1524784806000, 1000, 1000, 'FRRDELTAVAR',
20+
* null, null, 0, 'ACTIVE', null, null, null, 0, 30, 0, 0, null, 0,
21+
* 0.00207328
22+
* ]
23+
*
24+
* debug('spawning mock server...')
25+
*
26+
* const srv = new MockRESTv2Server({ listen: true })
27+
* const rest = new RESTv2({
28+
* apiKey: 'dummy',
29+
* apiSecret: 'dummy',
30+
* url: 'http://localhost:9999'
31+
* })
32+
*
33+
* srv.setResponse('f_offers.fUSD', [FUNDING_OFFER])
34+
*
35+
* debug('requesting preset response...')
36+
*
37+
* rest.fundingOffers('fUSD').then(([incomingFundingOffer]) => {
38+
* assert.deepStrictEqual(incomingFundingOffer, FUNDING_OFFER)
39+
*
40+
* debug('correct response received')
41+
* srv.close()
42+
*
43+
* return null
44+
* }).catch((e) => {
45+
* debug(`error: ${e.message}`)
46+
* })
47+
*/
48+
49+
module.exports = {
50+
MockServer,
51+
MockWSv2Server,
52+
MockRESTv2Server
53+
}

lib/rest2/index.js

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
'use strict'
2+
3+
const express = require('express')
4+
const Bluebird = require('bluebird')
5+
const bodyParser = require('body-parser')
6+
const _keys = require('lodash/keys')
7+
const _isUndefined = require('lodash/isUndefined')
8+
const debug = require('debug')('bfx:api-mock-srv:rest2')
9+
10+
const MockServer = require('../server')
11+
const RESTV2_MOCK_METHODS = require('./methods')
12+
13+
/**
14+
* REST v2 API server mock
15+
*
16+
* Exposes the same routes as the real API, and maps them to a response table.
17+
* Multiple potential responses can be defined for endpoints with arguments,
18+
* with the best match sent to clients on request.
19+
*
20+
* i.e. If the following responses are configured:
21+
* - `orders.tBTCUSD: [42]`
22+
* - `orders: [41]`
23+
*
24+
* A `GET` on `/v2/auth/r/orders/tBTCUSD/hist` would return `[42]`, but a query
25+
* for a different symbol (`tETHUSD`) would return `[41]`.
26+
*
27+
* @class
28+
* @memberof module:bfx-api-mock-srv
29+
* @augments module:bfx-api-mock-srv.MockServer
30+
*/
31+
class MockRESTv2Server extends MockServer {
32+
/**
33+
* @param {object} [args={}] - args
34+
* @param {number} [args.apiPort=9999] - API port number
35+
* @param {number} [args.cmdPort=9998] - command port number
36+
* @param {boolean} [args.listen=true] - enables auto listen()
37+
*/
38+
constructor (args = {}) {
39+
const { apiPort = 9999, cmdPort = 9998, listen = true } = args
40+
41+
super({ cmdPort })
42+
43+
this._apiServer = express()
44+
this._apiServer.use(bodyParser.json())
45+
this._apiServerHTTP = null
46+
this._apiPort = apiPort
47+
48+
_keys(RESTV2_MOCK_METHODS).forEach((route) => {
49+
const auth = route.split('/')[2] === 'auth'
50+
51+
this._generateRoute(
52+
auth ? 'post' : 'get', route, RESTV2_MOCK_METHODS[route]
53+
)
54+
})
55+
56+
if (listen) {
57+
this.listen()
58+
}
59+
}
60+
61+
/**
62+
* @static
63+
*
64+
* @param {express.Request} req - request
65+
* @param {string} routeKey - key
66+
* @returns {string[]} keys
67+
*/
68+
static keysForRoute (req, routeKey) {
69+
const args = Object.assign(
70+
{}, req.params || {}, req.query || {}, req.body || {}
71+
)
72+
73+
// Replace {tokens} with data values, where possible
74+
let tokens = routeKey.split('.').map((token) => {
75+
if (token[0] !== '{' || token[token.length - 1] !== '}') {
76+
return token
77+
}
78+
79+
const val = args[token.substring(1, token.length - 1)]
80+
81+
return _isUndefined(val) ? '' : val
82+
})
83+
84+
const keys = []
85+
86+
while (tokens.length > 0) {
87+
keys.push(tokens.join('.'))
88+
tokens = tokens.splice(0, tokens.length - 1)
89+
}
90+
91+
return keys
92+
}
93+
94+
/**
95+
* @private
96+
*
97+
* @param {string} type - 'post' or 'get'
98+
* @param {string} route - route
99+
* @param {string} routeKey - key
100+
*/
101+
_generateRoute (type, route, routeKey) {
102+
this._apiServer[type](route, (req, res) => {
103+
const keys = MockRESTv2Server.keysForRoute(req, routeKey)
104+
105+
// Check keys in order of token qty
106+
for (let i = 0; i < keys.length; i++) {
107+
if (this._responses.has(keys[i])) {
108+
const response = this._responses.get(keys[i])
109+
110+
if (!response) continue // could be null
111+
112+
try {
113+
return res.json(JSON.parse(response))
114+
} catch (err) {
115+
return res.status(500).json({
116+
error: 'bad response json'
117+
})
118+
}
119+
}
120+
}
121+
122+
return res.status(404).json({
123+
error: 'unknown arguments',
124+
keys
125+
})
126+
})
127+
}
128+
129+
/**
130+
* Starts the API server listening on the configured port. This is a no-op if
131+
* the server is already up
132+
*/
133+
listen () {
134+
if (this._apiServerHTTP) {
135+
return
136+
}
137+
138+
super.listen()
139+
this._apiServerHTTP = this._apiServer.listen(this._apiPort)
140+
141+
debug('rest2 api server listening on port %d', this._apiPort)
142+
}
143+
144+
/**
145+
* Closes the API server if it is running; This is a no-op if it is not.
146+
*
147+
* @async
148+
* @returns {Promise} p
149+
*/
150+
async close () {
151+
await super.close()
152+
153+
if (!this._apiServerHTTP) {
154+
return null
155+
}
156+
157+
return new Bluebird((resolve, reject) => {
158+
this._apiServerHTTP.close((err) => {
159+
if (err) {
160+
reject(err)
161+
return
162+
}
163+
164+
this._apiServerHTTP = null
165+
debug('rest2 api server closed')
166+
resolve()
167+
})
168+
})
169+
}
170+
}
171+
172+
module.exports = MockRESTv2Server

0 commit comments

Comments
 (0)