hmpo-model 3.2.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +21 -0
- package/README.md +86 -40
- package/lib/local-model.js +1 -1
- package/lib/remote-model.js +146 -92
- package/package.json +8 -6
- package/test/lib/spec.remote-model.js +298 -98
- package/.travis.yml +0 -8
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# This config is equivalent to both the '.circleci/extended/orb-free.yml' and the base '.circleci/config.yml'
|
|
2
|
+
version: 2.1
|
|
3
|
+
|
|
4
|
+
# Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
|
|
5
|
+
# See: https://circleci.com/docs/2.0/orb-intro/
|
|
6
|
+
orbs:
|
|
7
|
+
node: circleci/node@4.7
|
|
8
|
+
|
|
9
|
+
# Invoke jobs via workflows
|
|
10
|
+
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
|
|
11
|
+
workflows:
|
|
12
|
+
sample: # This is the name of the workflow, feel free to change it to better match your workflow.
|
|
13
|
+
# Inside the workflow, you define the jobs you want to run.
|
|
14
|
+
jobs:
|
|
15
|
+
- node/test:
|
|
16
|
+
# This is the node version to use for the `cimg/node` tag
|
|
17
|
+
# Relevant tags can be found on the CircleCI Developer Hub
|
|
18
|
+
# https://circleci.com/developer/images/image/cimg/node
|
|
19
|
+
version: '16.10'
|
|
20
|
+
# If you are using yarn, change the line below from "npm" to "yarn"
|
|
21
|
+
pkg-manager: npm
|
package/README.md
CHANGED
|
@@ -2,88 +2,134 @@
|
|
|
2
2
|
* localModel - Simple model for data persistance
|
|
3
3
|
* remoteModel - Simple model for interacting with http/rest apis.
|
|
4
4
|
|
|
5
|
+
## Upgrading
|
|
6
|
+
|
|
7
|
+
The deprecated `request` library has been replaced with `got`. The API is very similar, and some args are translated, like auth, and proxy.
|
|
8
|
+
The new `got` library doesn't automativally use the proxy environment variables so you would need to use something like `global-agent` in your
|
|
9
|
+
app if you need to specify proxies by environment arguments.
|
|
10
|
+
|
|
11
|
+
The `request` method no longer takes a body. This should be inserted as `json`, `body`, or `form` into the `requestConfig` method.
|
|
12
|
+
|
|
5
13
|
## Local Model Usage
|
|
6
|
-
### get
|
|
14
|
+
### `get(name)`
|
|
7
15
|
* gets a model property via a key
|
|
8
16
|
|
|
9
|
-
### set
|
|
17
|
+
### `set(name, value)` or `set({ name: value })`
|
|
10
18
|
* sets a property on the model to a value and dispatches events
|
|
11
19
|
|
|
12
|
-
### unset
|
|
20
|
+
### `unset(name)`
|
|
13
21
|
* unsets a property
|
|
14
22
|
|
|
15
|
-
### reset
|
|
23
|
+
### `reset([options])`
|
|
16
24
|
* resets a model
|
|
17
25
|
* suppresses `change` event notifications if `options.silent` is set
|
|
18
26
|
|
|
19
|
-
### increment
|
|
27
|
+
### `increment(name)`
|
|
20
28
|
* Increments a property
|
|
21
29
|
|
|
22
|
-
### toJSON
|
|
30
|
+
### `toJSON()`
|
|
23
31
|
* returns a JSON representation of the data in the model
|
|
24
32
|
|
|
25
33
|
## Remote Model Usage
|
|
26
34
|
|
|
27
35
|
Normally this would be used as an abstract class and extended with your own implementation.
|
|
28
36
|
|
|
29
|
-
Implementations would normally define at least a `url` method to define the target of API calls.
|
|
30
|
-
|
|
31
|
-
There are three methods for API interaction corresponding to GET, POST, and DELETE http methods:
|
|
32
|
-
|
|
33
|
-
### `fetch`
|
|
37
|
+
Implementations would normally define at least a `url():url` method to define the target of API calls.
|
|
34
38
|
|
|
39
|
+
Example implimentation:
|
|
35
40
|
```javascript
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
class MyModel extends HmpoModel {
|
|
42
|
+
url() {
|
|
43
|
+
return super.url('https://my.example.com/url')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
auth() {
|
|
47
|
+
return super.auth('username:password');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
requestConfig(config) {
|
|
51
|
+
config.proxy = 'http://proxy.example.com:3128'
|
|
52
|
+
return super.requestConfig(config);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// add data to JSON post body
|
|
56
|
+
prepare(callback) {
|
|
57
|
+
super.prepare((err, data) => {
|
|
58
|
+
if (err) return callback(err);
|
|
59
|
+
data.foo = 'bar';
|
|
60
|
+
callback(null, data);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// transform returned data
|
|
65
|
+
parse(data) {
|
|
66
|
+
data.additionalItem = true;
|
|
67
|
+
return super.parse(data);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const model = new MyModel();
|
|
72
|
+
model.set('boo', 'baz');
|
|
73
|
+
model.save((err, data, responseTime) => {
|
|
74
|
+
if (err) return console.error(err);
|
|
38
75
|
console.log(data);
|
|
39
76
|
});
|
|
40
77
|
```
|
|
41
78
|
|
|
42
|
-
|
|
79
|
+
There are three methods for API interaction corresponding to GET, POST, and DELETE http methods:
|
|
80
|
+
|
|
81
|
+
### `fetch([args, ][callback])`
|
|
82
|
+
|
|
83
|
+
`fetch` performs a `GET` request on the url
|
|
43
84
|
|
|
44
85
|
```javascript
|
|
45
|
-
|
|
46
|
-
model.
|
|
47
|
-
property: 'properties are sent as JSON request body by default'
|
|
48
|
-
});
|
|
49
|
-
model.save(function (err, data, responseTime) {
|
|
86
|
+
const model = new Model();
|
|
87
|
+
model.fetch((err, data, responseTime) => {
|
|
50
88
|
console.log(data);
|
|
51
89
|
});
|
|
52
90
|
```
|
|
91
|
+
#### Request
|
|
92
|
+
- Request args for the `got` library, can be set by overriding the `requestConfig({}):{}` method.
|
|
93
|
+
|
|
94
|
+
- The `url` can be configured either by setting a default in the model options or `requestConfig()` data, or by overriding the `url(default, args):url` method.
|
|
95
|
+
|
|
96
|
+
- `proxy`, `timeout`, and basic `auth` can be set in the same way, using model options, setting in `requestConfig()`, or by overriding a method.
|
|
97
|
+
- Specifying a `proxy` will set up a proxy tunneling `agent` for the request.
|
|
98
|
+
- Specifying a numeric `timeout` will set the same timeout for all `got` timeout values.
|
|
99
|
+
- Basic `auth` can be a colon separated string, or a `{username, password}` or `{user, pass}` object.
|
|
100
|
+
|
|
101
|
+
#### Response
|
|
102
|
+
- The returned body will be expected to be in JSON format.
|
|
103
|
+
- If `statusCode < 400` the JSON response will be set to the model.
|
|
104
|
+
This behaviour can be changed by overriding the `parse(data):data` method.
|
|
105
|
+
- If `statusCode >= 400` the data will be passed to the `parseError(statusCode, data):error` method, and the `fetch` callback will be called with the returned error.
|
|
106
|
+
- If response statuses need to be treated differently than the above, the `parseResponse(statusCode, data, cb)` method can be overridden.
|
|
107
|
+
- If the response body is not going to be JSON, the `handleResponse(response, cb)` method can be overridden.
|
|
108
|
+
|
|
109
|
+
### `save([args, ][callback])`
|
|
53
110
|
|
|
54
|
-
|
|
111
|
+
`save` performs a `POST` request on the url
|
|
55
112
|
|
|
56
113
|
```javascript
|
|
57
|
-
|
|
114
|
+
const model = new Model();
|
|
58
115
|
model.set({
|
|
59
|
-
property: '
|
|
116
|
+
property: 'properties are sent as JSON request body by default'
|
|
60
117
|
});
|
|
61
|
-
model.save(
|
|
118
|
+
model.save((err, data, responseTime) => {
|
|
62
119
|
console.log(data);
|
|
63
120
|
});
|
|
64
121
|
```
|
|
65
122
|
|
|
66
|
-
|
|
123
|
+
- By default the post body will be a JSON encoded object containing all attributes set to the model using, extracted using `model.toJSON()`. This behaviour can be changed by overriding the `prepare(callback(err, data))` method.
|
|
124
|
+
- The response and body will be treated the same way as the `fetch` request above.
|
|
67
125
|
|
|
68
|
-
|
|
69
|
-
var model = new Model();
|
|
70
|
-
model.delete(function (err, data) {
|
|
71
|
-
console.log(data);
|
|
72
|
-
});
|
|
73
|
-
```
|
|
126
|
+
### `delete([args, ][callback])`
|
|
74
127
|
|
|
75
|
-
|
|
128
|
+
`delete` performs a `DELETE` request on the url
|
|
76
129
|
|
|
77
130
|
```javascript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// make a GET request to http://example.com:3000/foo/bar
|
|
81
|
-
model.fetch({
|
|
82
|
-
protocol: 'http',
|
|
83
|
-
hostname: 'example.com',
|
|
84
|
-
port: 3000,
|
|
85
|
-
path: '/foo/bar'
|
|
86
|
-
}, function (err, data, responseTime) {
|
|
131
|
+
const model = new Model();
|
|
132
|
+
model.delete((err, data, responseTime) => {
|
|
87
133
|
console.log(data);
|
|
88
134
|
});
|
|
89
135
|
```
|
package/lib/local-model.js
CHANGED
package/lib/remote-model.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const debug = require('debug')('hmpo:model:remote');
|
|
3
4
|
const LocalModel = require('./local-model');
|
|
4
|
-
const
|
|
5
|
-
const _ = require('underscore');
|
|
5
|
+
const got = require('got');
|
|
6
6
|
const kebabCase = require('lodash.kebabcase');
|
|
7
|
+
const { URL } = require('url');
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TIMEOUT = 60000;
|
|
7
10
|
|
|
8
11
|
class RemoteModel extends LocalModel {
|
|
9
12
|
constructor(attributes, options) {
|
|
10
13
|
super(attributes, options);
|
|
11
|
-
|
|
14
|
+
this.got = got;
|
|
12
15
|
this.options.label = this.options.label || kebabCase(this.constructor.name);
|
|
13
16
|
this.setLogger();
|
|
14
17
|
}
|
|
@@ -19,78 +22,143 @@ class RemoteModel extends LocalModel {
|
|
|
19
22
|
this.logger = hmpoLogger.get(':' + this.options.label);
|
|
20
23
|
} catch (e) {
|
|
21
24
|
console.error('Error setting logger, using console instead!', e);
|
|
22
|
-
this.logger = { outbound: console.log, trimHtml:
|
|
25
|
+
this.logger = { outbound: console.log, trimHtml: html => html };
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
fetch(callback) {
|
|
27
|
-
|
|
29
|
+
fetch(args, callback) {
|
|
30
|
+
if (typeof args === 'function') {
|
|
31
|
+
callback = args;
|
|
32
|
+
args = undefined;
|
|
33
|
+
}
|
|
34
|
+
const config = this.requestConfig({method: 'GET'}, args);
|
|
28
35
|
this.request(config, callback);
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
save(callback) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
headers: {
|
|
41
|
-
'Content-Type': 'application/json',
|
|
42
|
-
'Content-Length': Buffer.byteLength(data)
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
this.request(config, data, callback);
|
|
47
|
-
|
|
38
|
+
save(args, callback) {
|
|
39
|
+
if (typeof args === 'function') {
|
|
40
|
+
callback = args;
|
|
41
|
+
args = undefined;
|
|
42
|
+
}
|
|
43
|
+
this.prepare((err, json) => {
|
|
44
|
+
if (err) return callback(err);
|
|
45
|
+
const config = this.requestConfig({method: 'POST', json}, args);
|
|
46
|
+
this.request(config, callback);
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
delete(callback) {
|
|
52
|
-
|
|
50
|
+
delete(args, callback) {
|
|
51
|
+
if (typeof args === 'function') {
|
|
52
|
+
callback = args;
|
|
53
|
+
args = undefined;
|
|
54
|
+
}
|
|
55
|
+
const config = this.requestConfig({method: 'DELETE'}, args);
|
|
53
56
|
this.request(config, callback);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
prepare(callback) {
|
|
60
|
+
debug('prepare');
|
|
61
|
+
callback(null, this.toJSON());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
requestConfig(config, args) {
|
|
65
|
+
const retConfig = Object.assign({}, config);
|
|
58
66
|
|
|
59
|
-
retConfig.
|
|
67
|
+
retConfig.url = this.url(retConfig.url || retConfig.uri, args);
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
retConfig.timeout = this.timeout(retConfig.timeout);
|
|
62
70
|
|
|
71
|
+
const auth = this.auth(retConfig.auth);
|
|
63
72
|
if (auth) {
|
|
64
|
-
retConfig.
|
|
73
|
+
retConfig.username = auth.username || auth.user;
|
|
74
|
+
retConfig.password = auth.password || auth.pass;
|
|
65
75
|
}
|
|
76
|
+
delete retConfig.auth;
|
|
66
77
|
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
const agent = this.proxy(retConfig.proxy, retConfig.url);
|
|
79
|
+
if (agent) {
|
|
80
|
+
retConfig.agent = agent;
|
|
69
81
|
}
|
|
82
|
+
delete retConfig.proxy;
|
|
83
|
+
|
|
84
|
+
const headers = Object.assign({}, this.options.headers, retConfig.headers);
|
|
85
|
+
if (Object.keys(headers).length) retConfig.headers = headers;
|
|
86
|
+
|
|
87
|
+
debug('requestConfig', retConfig);
|
|
70
88
|
|
|
71
89
|
return retConfig;
|
|
72
90
|
}
|
|
73
91
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
url(url = this.options.url) {
|
|
93
|
+
return url;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
auth(auth = this.options.auth) {
|
|
97
|
+
if (typeof auth === 'string') {
|
|
98
|
+
const splitAuth = auth.split(':');
|
|
99
|
+
auth = {
|
|
100
|
+
username: splitAuth.shift(),
|
|
101
|
+
password: splitAuth.join(':')
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return auth;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
timeout(timeout = this.options.timeout || DEFAULT_TIMEOUT) {
|
|
108
|
+
if (typeof timeout === 'number') {
|
|
109
|
+
timeout = {
|
|
110
|
+
lookup: timeout,
|
|
111
|
+
connect: timeout,
|
|
112
|
+
secureConnect: timeout,
|
|
113
|
+
socket: timeout,
|
|
114
|
+
send: timeout,
|
|
115
|
+
response: timeout
|
|
116
|
+
};
|
|
78
117
|
}
|
|
118
|
+
return timeout;
|
|
119
|
+
}
|
|
79
120
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
let requestSettings = _.clone(settings);
|
|
83
|
-
requestSettings.body = body;
|
|
121
|
+
proxy(proxy = this.options.proxy, url) {
|
|
122
|
+
if (!proxy || !url) return;
|
|
84
123
|
|
|
85
|
-
|
|
124
|
+
if (typeof proxy === 'string') proxy = { proxy };
|
|
86
125
|
|
|
87
|
-
|
|
126
|
+
const isHttps = (new URL(url).protocol === 'https:');
|
|
88
127
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
if (isHttps) {
|
|
129
|
+
const { HttpsProxyAgent } = require('hpagent');
|
|
130
|
+
return {
|
|
131
|
+
https: new HttpsProxyAgent(Object.assign({
|
|
132
|
+
keepAlive: false,
|
|
133
|
+
maxSockets: 1,
|
|
134
|
+
maxFreeSockets: 1,
|
|
135
|
+
}, proxy))
|
|
136
|
+
};
|
|
137
|
+
} else {
|
|
138
|
+
const { HttpProxyAgent } = require('hpagent');
|
|
139
|
+
return {
|
|
140
|
+
http: new HttpProxyAgent(Object.assign({
|
|
141
|
+
keepAlive: false,
|
|
142
|
+
maxSockets: 1,
|
|
143
|
+
maxFreeSockets: 1,
|
|
144
|
+
}, proxy))
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
93
148
|
|
|
149
|
+
request(settings, callback) {
|
|
150
|
+
this.hookSync({settings});
|
|
151
|
+
this.logSync({settings});
|
|
152
|
+
this.emit('sync', settings);
|
|
153
|
+
|
|
154
|
+
let responseTime;
|
|
155
|
+
const startTime = process.hrtime.bigint();
|
|
156
|
+
const setResponseTime = () => {
|
|
157
|
+
const endTime = process.hrtime.bigint();
|
|
158
|
+
responseTime = Number((Number(endTime - startTime) / 1000000).toFixed(3));
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const _callback = (err, data, statusCode) => {
|
|
94
162
|
if (err) {
|
|
95
163
|
this.hookFail({settings, statusCode, responseTime, err, data});
|
|
96
164
|
this.logError({settings, statusCode, responseTime, err, data});
|
|
@@ -105,25 +173,27 @@ class RemoteModel extends LocalModel {
|
|
|
105
173
|
}
|
|
106
174
|
};
|
|
107
175
|
|
|
108
|
-
|
|
109
|
-
|
|
176
|
+
this.got(settings)
|
|
177
|
+
.catch(err => {
|
|
178
|
+
debug('request got error', err);
|
|
179
|
+
setResponseTime();
|
|
110
180
|
if (err.code === 'ETIMEDOUT') {
|
|
111
181
|
err.message = 'Connection timed out';
|
|
112
182
|
err.status = 504;
|
|
113
183
|
}
|
|
114
|
-
err.status = err.status || (response && response.statusCode) || 503;
|
|
115
|
-
return _callback(err, null, err.status);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.emit('sync', settings);
|
|
184
|
+
err.status = err.status || (err.response && err.response.statusCode) || 503;
|
|
185
|
+
return _callback(err, null, err.status, err.response);
|
|
186
|
+
})
|
|
187
|
+
.then(response => {
|
|
188
|
+
debug('request got response', response);
|
|
189
|
+
setResponseTime();
|
|
190
|
+
this.handleResponse(response, _callback);
|
|
191
|
+
});
|
|
123
192
|
}
|
|
124
193
|
|
|
125
194
|
handleResponse(response, callback) {
|
|
126
|
-
|
|
195
|
+
debug('handleResponse', response);
|
|
196
|
+
let data;
|
|
127
197
|
try {
|
|
128
198
|
data = JSON.parse(response.body || '{}');
|
|
129
199
|
} catch (err) {
|
|
@@ -135,53 +205,37 @@ class RemoteModel extends LocalModel {
|
|
|
135
205
|
}
|
|
136
206
|
|
|
137
207
|
parseResponse(statusCode, data, callback) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
callback(err, null, statusCode);
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
callback(this.parseError(statusCode, data), data, statusCode);
|
|
208
|
+
debug('parseResponse', statusCode, data);
|
|
209
|
+
|
|
210
|
+
if (statusCode >= 400) {
|
|
211
|
+
const error = this.parseError(statusCode, data);
|
|
212
|
+
return callback(error, data, statusCode);
|
|
147
213
|
}
|
|
148
|
-
}
|
|
149
214
|
|
|
150
|
-
|
|
151
|
-
|
|
215
|
+
try {
|
|
216
|
+
data = this.parse(data);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return callback(err, null, statusCode);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
callback(null, data, statusCode);
|
|
152
222
|
}
|
|
153
223
|
|
|
154
224
|
parse(data) {
|
|
225
|
+
debug('parse', data);
|
|
226
|
+
if (data && typeof data === 'object') this.set(data);
|
|
155
227
|
return data;
|
|
156
228
|
}
|
|
157
229
|
|
|
158
230
|
parseError(statusCode, data) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
url() {
|
|
163
|
-
return this.options.url;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
auth(credentials) {
|
|
167
|
-
if (!credentials) return;
|
|
168
|
-
|
|
169
|
-
if (typeof credentials === 'string') {
|
|
170
|
-
let auth = credentials.split(':');
|
|
171
|
-
credentials = {
|
|
172
|
-
user: auth.shift(),
|
|
173
|
-
pass: auth.join(':'),
|
|
174
|
-
sendImmediately: true
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return credentials;
|
|
231
|
+
debug('parseError, statusCode, data');
|
|
232
|
+
return Object.assign({ status: statusCode }, data);
|
|
179
233
|
}
|
|
180
234
|
|
|
181
235
|
logMeta(tokenData) {
|
|
182
236
|
let data = {
|
|
183
237
|
outVerb: tokenData.settings.method,
|
|
184
|
-
outRequest: tokenData.settings.
|
|
238
|
+
outRequest: tokenData.settings.url
|
|
185
239
|
};
|
|
186
240
|
|
|
187
241
|
if (tokenData.statusCode) {
|
|
@@ -199,7 +253,7 @@ class RemoteModel extends LocalModel {
|
|
|
199
253
|
data.outErrorBody = this.logger.trimHtml(tokenData.err.body);
|
|
200
254
|
}
|
|
201
255
|
|
|
202
|
-
|
|
256
|
+
Object.assign(data, this.options.logging);
|
|
203
257
|
|
|
204
258
|
return data;
|
|
205
259
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hmpo-model",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Simple model for interacting with http/rest apis.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,19 +25,21 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/UKHomeOffice/passports-model",
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"debug": "^4.3.3",
|
|
29
|
+
"got": "^11.8.3",
|
|
30
|
+
"hpagent": "^0.1.2",
|
|
28
31
|
"lodash.kebabcase": "^4.1.1",
|
|
29
|
-
"request": "^2.88.2",
|
|
30
32
|
"underscore": "^1.13.1"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"chai": "^4.3.4",
|
|
34
|
-
"eslint": "^
|
|
36
|
+
"eslint": "^8.3.0",
|
|
35
37
|
"hmpo-logger": "^4.1.3",
|
|
36
|
-
"mocha": "^
|
|
38
|
+
"mocha": "^9.1.3",
|
|
37
39
|
"nyc": "^15.1.0",
|
|
38
40
|
"proxyquire": "^2.0.0",
|
|
39
|
-
"sinon": "^
|
|
40
|
-
"sinon-chai": "^3.
|
|
41
|
+
"sinon": "^12.0.1",
|
|
42
|
+
"sinon-chai": "^3.7.0"
|
|
41
43
|
},
|
|
42
44
|
"nyc": {
|
|
43
45
|
"all": true,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Model = require('../../lib/remote-model');
|
|
4
4
|
const BaseModel = require('../../lib/local-model');
|
|
5
5
|
const _ = require('underscore');
|
|
6
|
+
const logger = require('hmpo-logger');
|
|
7
|
+
|
|
8
|
+
const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
|
|
6
9
|
|
|
7
10
|
describe('Remote Model', () => {
|
|
8
|
-
let model,
|
|
11
|
+
let model, cb, mocks;
|
|
9
12
|
|
|
10
13
|
beforeEach(() => {
|
|
11
|
-
Model = require('../../lib/remote-model');
|
|
12
14
|
model = new Model();
|
|
13
15
|
|
|
14
16
|
cb = sinon.stub();
|
|
@@ -24,7 +26,6 @@ describe('Remote Model', () => {
|
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
it('should be an instance of LocalModel', () => {
|
|
27
|
-
Model = require('../../lib/remote-model');
|
|
28
29
|
model = new Model();
|
|
29
30
|
|
|
30
31
|
model.should.be.an.instanceOf(BaseModel);
|
|
@@ -54,35 +55,30 @@ describe('Remote Model', () => {
|
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
describe('setLogger', () => {
|
|
57
|
-
let getStub, Model;
|
|
58
|
-
|
|
59
58
|
beforeEach(() => {
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
sinon.stub(logger, 'get').returns('logger');
|
|
60
|
+
});
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
get: getStub
|
|
66
|
-
}
|
|
67
|
-
});
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
logger.get.restore();
|
|
68
64
|
});
|
|
69
65
|
|
|
70
66
|
it('should set up a new hmpo-logger', () => {
|
|
71
67
|
model = new Model();
|
|
72
68
|
|
|
73
|
-
|
|
69
|
+
logger.get.should.have.been.calledWithExactly(':remote-model');
|
|
74
70
|
model.logger.should.equal('logger');
|
|
75
71
|
});
|
|
76
72
|
|
|
77
|
-
it('should use console if hmpo-logger is not available', () => {
|
|
78
|
-
|
|
73
|
+
it('should use console log and a trimHtml pass-through if hmpo-logger is not available', () => {
|
|
74
|
+
logger.get.throws(new Error());
|
|
79
75
|
|
|
80
76
|
model = new Model();
|
|
81
77
|
|
|
82
|
-
model.logger.should.eql(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
model.logger.outbound.should.eql(console.log);
|
|
79
|
+
model.logger.trimHtml.should.be.a('function');
|
|
80
|
+
const html = {};
|
|
81
|
+
model.logger.trimHtml(html).should.equal(html);
|
|
86
82
|
});
|
|
87
83
|
});
|
|
88
84
|
|
|
@@ -104,6 +100,15 @@ describe('Remote Model', () => {
|
|
|
104
100
|
method: 'GET'
|
|
105
101
|
});
|
|
106
102
|
});
|
|
103
|
+
|
|
104
|
+
it('should pass args onto requestConfig', () => {
|
|
105
|
+
model.fetch({ foo: 'bar' }, cb);
|
|
106
|
+
|
|
107
|
+
model.requestConfig.should.have.been.calledWithExactly({
|
|
108
|
+
method: 'GET'
|
|
109
|
+
}, { foo: 'bar' });
|
|
110
|
+
});
|
|
111
|
+
|
|
107
112
|
it('should call request', () => {
|
|
108
113
|
model.requestConfig.returns(mocks.config);
|
|
109
114
|
|
|
@@ -129,9 +134,9 @@ describe('Remote Model', () => {
|
|
|
129
134
|
it('should call prepare', () => {
|
|
130
135
|
model.save(cb);
|
|
131
136
|
|
|
132
|
-
model.prepare.should.have.been.
|
|
133
|
-
|
|
137
|
+
model.prepare.should.have.been.calledWithExactly(sinon.match.func);
|
|
134
138
|
});
|
|
139
|
+
|
|
135
140
|
it('should call callback with an error', () => {
|
|
136
141
|
let error = new Error('error');
|
|
137
142
|
model.prepare.yields(error);
|
|
@@ -139,7 +144,6 @@ describe('Remote Model', () => {
|
|
|
139
144
|
model.save(cb);
|
|
140
145
|
|
|
141
146
|
cb.should.have.been.calledWith(error);
|
|
142
|
-
|
|
143
147
|
});
|
|
144
148
|
|
|
145
149
|
context('on prepare success', () => {
|
|
@@ -155,20 +159,27 @@ describe('Remote Model', () => {
|
|
|
155
159
|
it('should use requestConfig', () => {
|
|
156
160
|
model.save(cb);
|
|
157
161
|
|
|
158
|
-
model.requestConfig.should.have.been.
|
|
162
|
+
model.requestConfig.should.have.been.calledWithExactly({
|
|
159
163
|
method: 'POST',
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
json: preparedData
|
|
165
|
+
}, undefined);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should pass args onto requestConfig', () => {
|
|
169
|
+
model.save({ foo: 'bar' }, cb);
|
|
170
|
+
|
|
171
|
+
model.requestConfig.should.have.been.calledWithExactly({
|
|
172
|
+
method: 'POST',
|
|
173
|
+
json: preparedData
|
|
174
|
+
}, { foo: 'bar' });
|
|
165
175
|
});
|
|
176
|
+
|
|
166
177
|
it('should call request', () => {
|
|
167
178
|
model.requestConfig.returns(mocks.config);
|
|
168
179
|
|
|
169
180
|
model.save(cb);
|
|
170
181
|
|
|
171
|
-
model.request.should.have.been.calledWith(mocks.config,
|
|
182
|
+
model.request.should.have.been.calledWith(mocks.config, cb);
|
|
172
183
|
});
|
|
173
184
|
});
|
|
174
185
|
|
|
@@ -192,6 +203,15 @@ describe('Remote Model', () => {
|
|
|
192
203
|
method: 'DELETE'
|
|
193
204
|
});
|
|
194
205
|
});
|
|
206
|
+
|
|
207
|
+
it('should pass args onto requestConfig', () => {
|
|
208
|
+
model.delete({ foo: 'bar' }, cb);
|
|
209
|
+
|
|
210
|
+
model.requestConfig.should.have.been.calledWithExactly({
|
|
211
|
+
method: 'DELETE'
|
|
212
|
+
}, { foo: 'bar' });
|
|
213
|
+
});
|
|
214
|
+
|
|
195
215
|
it('should call request', () => {
|
|
196
216
|
model.requestConfig.returns(mocks.config);
|
|
197
217
|
|
|
@@ -202,50 +222,227 @@ describe('Remote Model', () => {
|
|
|
202
222
|
});
|
|
203
223
|
|
|
204
224
|
describe('requestConfig', () => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
225
|
+
describe('url', () => {
|
|
226
|
+
it('should use url from model options', () => {
|
|
227
|
+
model.options.url = 'https://example.com/options';
|
|
228
|
+
const config = model.requestConfig({
|
|
229
|
+
'method': 'VERB'
|
|
230
|
+
});
|
|
208
231
|
|
|
209
|
-
|
|
210
|
-
|
|
232
|
+
config.url.should.equal('https://example.com/options');
|
|
233
|
+
});
|
|
211
234
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
it('should use url from request config', () => {
|
|
236
|
+
model.options.url = 'https://example.com/options';
|
|
237
|
+
const config = model.requestConfig({
|
|
238
|
+
'method': 'VERB',
|
|
239
|
+
'url': 'https://example.com/config'
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
config.url.should.equal('https://example.com/config');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should use url returned by overridden url() method', () => {
|
|
246
|
+
model.options.url = 'https://example.com/options';
|
|
247
|
+
model.url = sinon.stub().returns('https://example.com/overridden');
|
|
248
|
+
const config = model.requestConfig({
|
|
249
|
+
'method': 'VERB',
|
|
250
|
+
'url': 'https://example.com/config'
|
|
251
|
+
});
|
|
252
|
+
model.url.should.have.been.calledWithExactly('https://example.com/config', undefined);
|
|
253
|
+
config.url.should.equal('https://example.com/overridden');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should pass args onto url method', () => {
|
|
257
|
+
model.url = sinon.stub().returns('https://example.com/overridden');
|
|
258
|
+
const config = model.requestConfig({
|
|
259
|
+
'method': 'VERB',
|
|
260
|
+
'url': 'https://example.com/config'
|
|
261
|
+
}, { foo: 'bar' });
|
|
262
|
+
model.url.should.have.been.calledWithExactly('https://example.com/config', { foo: 'bar' });
|
|
263
|
+
config.url.should.equal('https://example.com/overridden');
|
|
264
|
+
});
|
|
215
265
|
});
|
|
216
266
|
|
|
267
|
+
describe('auth', () => {
|
|
268
|
+
it('should use auth from model options', () => {
|
|
269
|
+
model.options.auth = 'options:pass:word';
|
|
270
|
+
const config = model.requestConfig({
|
|
271
|
+
'method': 'VERB'
|
|
272
|
+
});
|
|
217
273
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
'
|
|
274
|
+
config.username.should.equal('options');
|
|
275
|
+
config.password.should.equal('pass:word');
|
|
276
|
+
config.should.not.have.property('auth');
|
|
221
277
|
});
|
|
222
278
|
|
|
223
|
-
|
|
279
|
+
it('should use auth from config', () => {
|
|
280
|
+
model.options.auth = 'options:pass:word';
|
|
281
|
+
const config = model.requestConfig({
|
|
282
|
+
'method': 'VERB',
|
|
283
|
+
'auth': 'config:pass:word'
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
config.username.should.equal('config');
|
|
287
|
+
config.password.should.equal('pass:word');
|
|
288
|
+
config.should.not.have.property('auth');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should use auth from overidden auth() method', () => {
|
|
292
|
+
model.options.auth = 'options:pass:word';
|
|
293
|
+
model.auth = sinon.stub().returns({ user: 'overridden', pass: 'pass:word' });
|
|
294
|
+
const config = model.requestConfig({
|
|
295
|
+
'method': 'VERB',
|
|
296
|
+
'auth': 'config:pass:word'
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
model.auth.should.have.been.calledWithExactly('config:pass:word');
|
|
300
|
+
config.username.should.equal('overridden');
|
|
301
|
+
config.password.should.equal('pass:word');
|
|
302
|
+
config.should.not.have.property('auth');
|
|
303
|
+
});
|
|
224
304
|
});
|
|
225
305
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
306
|
+
describe('timeout', () => {
|
|
307
|
+
it('should use a default timeout', () => {
|
|
308
|
+
const config = model.requestConfig({
|
|
309
|
+
'method': 'VERB'
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
config.should.deep.include({
|
|
313
|
+
timeout: {
|
|
314
|
+
connect: 60000,
|
|
315
|
+
lookup: 60000,
|
|
316
|
+
response: 60000,
|
|
317
|
+
secureConnect: 60000,
|
|
318
|
+
send: 60000,
|
|
319
|
+
socket: 60000
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should use timeout from model options', () => {
|
|
325
|
+
model.options.timeout = 1000;
|
|
326
|
+
const config = model.requestConfig({
|
|
327
|
+
'method': 'VERB'
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
config.should.deep.include({
|
|
331
|
+
timeout: {
|
|
332
|
+
connect: 1000,
|
|
333
|
+
lookup: 1000,
|
|
334
|
+
response: 1000,
|
|
335
|
+
secureConnect: 1000,
|
|
336
|
+
send: 1000,
|
|
337
|
+
socket: 1000
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should use timeout from config', () => {
|
|
343
|
+
model.options.timeout = 1000;
|
|
344
|
+
const config = model.requestConfig({
|
|
345
|
+
'method': 'VERB',
|
|
346
|
+
'timeout': 2000
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
config.should.deep.include({
|
|
350
|
+
timeout: {
|
|
351
|
+
connect: 2000,
|
|
352
|
+
lookup: 2000,
|
|
353
|
+
response: 2000,
|
|
354
|
+
secureConnect: 2000,
|
|
355
|
+
send: 2000,
|
|
356
|
+
socket: 2000
|
|
357
|
+
}
|
|
358
|
+
});
|
|
229
359
|
});
|
|
230
360
|
|
|
231
|
-
|
|
361
|
+
it('should use timeout from specified object', () => {
|
|
362
|
+
const config = model.requestConfig({
|
|
363
|
+
'method': 'VERB',
|
|
364
|
+
'timeout': { connect: 3000 }
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
config.should.deep.include({
|
|
368
|
+
timeout: {
|
|
369
|
+
connect: 3000,
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should use timeout from overidden timeout() method', () => {
|
|
375
|
+
model.timeout = sinon.stub().returns({ connect: 4000 });
|
|
376
|
+
const config = model.requestConfig({
|
|
377
|
+
'method': 'VERB',
|
|
378
|
+
'timeout': 2000
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
model.timeout.should.have.been.calledWithExactly(2000);
|
|
382
|
+
config.should.deep.include({
|
|
383
|
+
timeout: {
|
|
384
|
+
connect: 4000,
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
});
|
|
232
388
|
});
|
|
233
389
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
390
|
+
describe('proxy', () => {
|
|
391
|
+
it('should not set up http proxy if there is no url', () => {
|
|
392
|
+
const returnedConfig = model.requestConfig({
|
|
393
|
+
'method': 'VERB',
|
|
394
|
+
'proxy': 'http://proxy.example.com:8000'
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
returnedConfig.should.not.have.property('proxy');
|
|
398
|
+
returnedConfig.should.not.have.property('agent');
|
|
238
399
|
});
|
|
239
400
|
|
|
240
|
-
|
|
241
|
-
|
|
401
|
+
it('should set up http proxy if specified', () => {
|
|
402
|
+
const returnedConfig = model.requestConfig({
|
|
403
|
+
'method': 'VERB',
|
|
404
|
+
'url': 'http://example.net',
|
|
405
|
+
'proxy': 'http://proxy.example.com:8000'
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
sinon.assert.match(returnedConfig, {
|
|
409
|
+
agent: {
|
|
410
|
+
http: sinon.match.instanceOf(HttpProxyAgent)
|
|
411
|
+
}
|
|
412
|
+
});
|
|
242
413
|
});
|
|
243
414
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
415
|
+
it('should set up https proxy if specified', () => {
|
|
416
|
+
const returnedConfig = model.requestConfig({
|
|
417
|
+
'method': 'VERB',
|
|
418
|
+
'url': 'https://example.net',
|
|
419
|
+
'proxy': 'http://proxy.example.com:8000'
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
sinon.assert.match(returnedConfig, {
|
|
423
|
+
agent: {
|
|
424
|
+
https: sinon.match.instanceOf(HttpsProxyAgent)
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('should pass proxy options to the new proxy', () => {
|
|
430
|
+
const returnedConfig = model.requestConfig({
|
|
431
|
+
'method': 'VERB',
|
|
432
|
+
'url': 'http://example.net',
|
|
433
|
+
'proxy': {
|
|
434
|
+
proxy: 'http://proxy.example.com:8000',
|
|
435
|
+
keepAlive: true
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
sinon.assert.match(returnedConfig, {
|
|
440
|
+
agent: {
|
|
441
|
+
http: {
|
|
442
|
+
keepAlive: true
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
});
|
|
249
446
|
});
|
|
250
447
|
});
|
|
251
448
|
|
|
@@ -357,14 +554,15 @@ describe('Remote Model', () => {
|
|
|
357
554
|
});
|
|
358
555
|
|
|
359
556
|
describe('request', () => {
|
|
360
|
-
let settings,
|
|
557
|
+
let settings, requestSettings, mocks;
|
|
361
558
|
|
|
362
559
|
beforeEach(() => {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
560
|
+
mocks = {};
|
|
561
|
+
mocks.got = sinon.stub().returns(mocks);
|
|
562
|
+
mocks.then = sinon.stub().returns(mocks);
|
|
563
|
+
mocks.catch = sinon.stub().returns(mocks);
|
|
367
564
|
model = new Model();
|
|
565
|
+
model.got = mocks.got;
|
|
368
566
|
|
|
369
567
|
sinon.stub(model, 'logSync');
|
|
370
568
|
sinon.stub(model, 'logSuccess');
|
|
@@ -385,22 +583,11 @@ describe('Remote Model', () => {
|
|
|
385
583
|
model.emit.restore();
|
|
386
584
|
});
|
|
387
585
|
|
|
388
|
-
it('should invoke request with settings including a body', () => {
|
|
389
|
-
requestSettings.body = body;
|
|
390
|
-
|
|
391
|
-
model.request(settings, body, cb);
|
|
392
|
-
|
|
393
|
-
mocks.request.should.have.been.called;
|
|
394
|
-
mocks.request.should.have.been.calledWith(requestSettings);
|
|
395
|
-
});
|
|
396
|
-
|
|
397
586
|
it('should invoke request with request settings', () => {
|
|
398
|
-
requestSettings.body = undefined;
|
|
399
|
-
|
|
400
587
|
model.request(settings, cb);
|
|
401
588
|
|
|
402
|
-
mocks.
|
|
403
|
-
mocks.
|
|
589
|
+
mocks.got.should.have.been.called;
|
|
590
|
+
mocks.got.should.have.been.calledWith(requestSettings);
|
|
404
591
|
});
|
|
405
592
|
|
|
406
593
|
it('should log sync messages', () => {
|
|
@@ -432,7 +619,7 @@ describe('Remote Model', () => {
|
|
|
432
619
|
});
|
|
433
620
|
|
|
434
621
|
it('should work without a callback', () => {
|
|
435
|
-
mocks.
|
|
622
|
+
mocks.catch.yields(new Error('Random Error'));
|
|
436
623
|
|
|
437
624
|
model.request(settings);
|
|
438
625
|
|
|
@@ -451,7 +638,9 @@ describe('Remote Model', () => {
|
|
|
451
638
|
'statusCode': 418
|
|
452
639
|
};
|
|
453
640
|
|
|
454
|
-
|
|
641
|
+
error.response = response;
|
|
642
|
+
|
|
643
|
+
mocks.catch.yields(error);
|
|
455
644
|
});
|
|
456
645
|
|
|
457
646
|
it('should log error messages', () => {
|
|
@@ -534,7 +723,7 @@ describe('Remote Model', () => {
|
|
|
534
723
|
|
|
535
724
|
context('on success', () => {
|
|
536
725
|
beforeEach(() => {
|
|
537
|
-
mocks.
|
|
726
|
+
mocks.then.yields({
|
|
538
727
|
'body': JSON.stringify({'data': 'value'}),
|
|
539
728
|
'statusCode': 200
|
|
540
729
|
});
|
|
@@ -641,6 +830,7 @@ describe('Remote Model', () => {
|
|
|
641
830
|
|
|
642
831
|
afterEach(() => {
|
|
643
832
|
model.parse.restore();
|
|
833
|
+
|
|
644
834
|
model.parseError.restore();
|
|
645
835
|
});
|
|
646
836
|
|
|
@@ -714,6 +904,17 @@ describe('Remote Model', () => {
|
|
|
714
904
|
it('returns data passed', () => {
|
|
715
905
|
model.parse({ data: 1 }).should.eql({ data: 1 });
|
|
716
906
|
});
|
|
907
|
+
|
|
908
|
+
it('sets the parsed data to the model', () => {
|
|
909
|
+
model.parse({ foo: 'bar' });
|
|
910
|
+
model.get('foo').should.equal('bar');
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
it('does not set if the data falsey', () => {
|
|
914
|
+
model.set = sinon.stub();
|
|
915
|
+
model.parse(null);
|
|
916
|
+
model.set.should.not.have.been.called;
|
|
917
|
+
});
|
|
717
918
|
});
|
|
718
919
|
|
|
719
920
|
describe('parseError', () => {
|
|
@@ -743,12 +944,11 @@ describe('Remote Model', () => {
|
|
|
743
944
|
});
|
|
744
945
|
|
|
745
946
|
it('should return parsed credentials if credentials is a string', () => {
|
|
746
|
-
let credentials = model.auth('username:
|
|
947
|
+
let credentials = model.auth('username:pass:word');
|
|
747
948
|
|
|
748
949
|
credentials.should.deep.equal({
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
sendImmediately: true
|
|
950
|
+
username: 'username',
|
|
951
|
+
password: 'pass:word'
|
|
752
952
|
});
|
|
753
953
|
|
|
754
954
|
});
|
|
@@ -767,21 +967,21 @@ describe('Remote Model', () => {
|
|
|
767
967
|
|
|
768
968
|
|
|
769
969
|
describe('logging', () => {
|
|
770
|
-
let
|
|
970
|
+
let mocks;
|
|
771
971
|
|
|
772
972
|
beforeEach(() => {
|
|
773
|
-
|
|
973
|
+
mocks = {
|
|
774
974
|
outbound: sinon.stub(),
|
|
775
975
|
trimHtml: sinon.stub()
|
|
776
976
|
};
|
|
777
|
-
|
|
778
|
-
let Model = proxyquire('../../lib/remote-model', {
|
|
779
|
-
'hmpo-logger': {
|
|
780
|
-
get: () => logger,
|
|
781
|
-
}
|
|
782
|
-
});
|
|
977
|
+
sinon.stub(logger, 'get').returns(mocks);
|
|
783
978
|
|
|
784
979
|
model = new Model();
|
|
980
|
+
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
afterEach(() => {
|
|
984
|
+
logger.get.restore();
|
|
785
985
|
});
|
|
786
986
|
|
|
787
987
|
describe('logSync', () => {
|
|
@@ -789,7 +989,7 @@ describe('Remote Model', () => {
|
|
|
789
989
|
let args = {
|
|
790
990
|
settings: {
|
|
791
991
|
method: 'VERB',
|
|
792
|
-
|
|
992
|
+
url: 'http://example.org'
|
|
793
993
|
}
|
|
794
994
|
};
|
|
795
995
|
|
|
@@ -800,7 +1000,7 @@ describe('Remote Model', () => {
|
|
|
800
1000
|
|
|
801
1001
|
model.logSync(args);
|
|
802
1002
|
|
|
803
|
-
|
|
1003
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
804
1004
|
'Model request sent :outVerb :outRequest',
|
|
805
1005
|
argsAsMeta
|
|
806
1006
|
);
|
|
@@ -812,7 +1012,7 @@ describe('Remote Model', () => {
|
|
|
812
1012
|
let args = {
|
|
813
1013
|
settings: {
|
|
814
1014
|
method: 'VERB',
|
|
815
|
-
|
|
1015
|
+
url: 'http://example.org'
|
|
816
1016
|
},
|
|
817
1017
|
statusCode: 418,
|
|
818
1018
|
responseTime: 1000
|
|
@@ -827,7 +1027,7 @@ describe('Remote Model', () => {
|
|
|
827
1027
|
|
|
828
1028
|
model.logError(args);
|
|
829
1029
|
|
|
830
|
-
|
|
1030
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
831
1031
|
'Model request failed :outVerb :outRequest :outResponseCode :outError',
|
|
832
1032
|
argsAsMeta
|
|
833
1033
|
);
|
|
@@ -839,7 +1039,7 @@ describe('Remote Model', () => {
|
|
|
839
1039
|
let args = {
|
|
840
1040
|
settings: {
|
|
841
1041
|
method: 'VERB',
|
|
842
|
-
|
|
1042
|
+
url: 'http://example.org'
|
|
843
1043
|
},
|
|
844
1044
|
statusCode: 418,
|
|
845
1045
|
responseTime: 1000
|
|
@@ -854,7 +1054,7 @@ describe('Remote Model', () => {
|
|
|
854
1054
|
|
|
855
1055
|
model.logSuccess(args);
|
|
856
1056
|
|
|
857
|
-
|
|
1057
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
858
1058
|
'Model request success :outVerb :outRequest :outResponseCode',
|
|
859
1059
|
argsAsMeta
|
|
860
1060
|
);
|
|
@@ -869,7 +1069,7 @@ describe('Remote Model', () => {
|
|
|
869
1069
|
data = {
|
|
870
1070
|
settings: {
|
|
871
1071
|
method: 'VERB',
|
|
872
|
-
|
|
1072
|
+
url: 'http://example.org'
|
|
873
1073
|
},
|
|
874
1074
|
statusCode: 418,
|
|
875
1075
|
responseTime: 3000,
|
|
@@ -944,7 +1144,7 @@ describe('Remote Model', () => {
|
|
|
944
1144
|
});
|
|
945
1145
|
|
|
946
1146
|
it('should be present with error', () => {
|
|
947
|
-
|
|
1147
|
+
mocks.trimHtml.returns('Html Body');
|
|
948
1148
|
|
|
949
1149
|
let result = model.logMeta(data);
|
|
950
1150
|
|