hmpo-model 3.1.0 → 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 +154 -94
- package/package.json +11 -9
- package/test/lib/spec.remote-model.js +320 -92
- 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,96 +1,170 @@
|
|
|
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
|
|
7
|
+
const { URL } = require('url');
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TIMEOUT = 60000;
|
|
8
10
|
|
|
9
11
|
class RemoteModel extends LocalModel {
|
|
10
12
|
constructor(attributes, options) {
|
|
11
13
|
super(attributes, options);
|
|
12
|
-
|
|
14
|
+
this.got = got;
|
|
15
|
+
this.options.label = this.options.label || kebabCase(this.constructor.name);
|
|
13
16
|
this.setLogger();
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
setLogger() {
|
|
17
|
-
|
|
20
|
+
try {
|
|
21
|
+
const hmpoLogger = require('hmpo-logger');
|
|
22
|
+
this.logger = hmpoLogger.get(':' + this.options.label);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error('Error setting logger, using console instead!', e);
|
|
25
|
+
this.logger = { outbound: console.log, trimHtml: html => html };
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
fetch(callback) {
|
|
21
|
-
|
|
29
|
+
fetch(args, callback) {
|
|
30
|
+
if (typeof args === 'function') {
|
|
31
|
+
callback = args;
|
|
32
|
+
args = undefined;
|
|
33
|
+
}
|
|
34
|
+
const config = this.requestConfig({method: 'GET'}, args);
|
|
22
35
|
this.request(config, callback);
|
|
23
36
|
}
|
|
24
37
|
|
|
25
|
-
save(callback) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/json',
|
|
36
|
-
'Content-Length': Buffer.byteLength(data)
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
this.request(config, data, callback);
|
|
41
|
-
|
|
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);
|
|
42
47
|
});
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
delete(callback) {
|
|
46
|
-
|
|
50
|
+
delete(args, callback) {
|
|
51
|
+
if (typeof args === 'function') {
|
|
52
|
+
callback = args;
|
|
53
|
+
args = undefined;
|
|
54
|
+
}
|
|
55
|
+
const config = this.requestConfig({method: 'DELETE'}, args);
|
|
47
56
|
this.request(config, callback);
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
prepare(callback) {
|
|
60
|
+
debug('prepare');
|
|
61
|
+
callback(null, this.toJSON());
|
|
62
|
+
}
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
requestConfig(config, args) {
|
|
65
|
+
const retConfig = Object.assign({}, config);
|
|
54
66
|
|
|
55
|
-
|
|
67
|
+
retConfig.url = this.url(retConfig.url || retConfig.uri, args);
|
|
56
68
|
|
|
69
|
+
retConfig.timeout = this.timeout(retConfig.timeout);
|
|
70
|
+
|
|
71
|
+
const auth = this.auth(retConfig.auth);
|
|
57
72
|
if (auth) {
|
|
58
|
-
retConfig.
|
|
73
|
+
retConfig.username = auth.username || auth.user;
|
|
74
|
+
retConfig.password = auth.password || auth.pass;
|
|
59
75
|
}
|
|
76
|
+
delete retConfig.auth;
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
const agent = this.proxy(retConfig.proxy, retConfig.url);
|
|
79
|
+
if (agent) {
|
|
80
|
+
retConfig.agent = agent;
|
|
63
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);
|
|
64
88
|
|
|
65
89
|
return retConfig;
|
|
66
90
|
}
|
|
67
91
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
};
|
|
72
117
|
}
|
|
118
|
+
return timeout;
|
|
119
|
+
}
|
|
73
120
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let requestSettings = _.clone(settings);
|
|
77
|
-
requestSettings.body = body;
|
|
121
|
+
proxy(proxy = this.options.proxy, url) {
|
|
122
|
+
if (!proxy || !url) return;
|
|
78
123
|
|
|
79
|
-
|
|
124
|
+
if (typeof proxy === 'string') proxy = { proxy };
|
|
80
125
|
|
|
81
|
-
|
|
126
|
+
const isHttps = (new URL(url).protocol === 'https:');
|
|
82
127
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
+
}
|
|
87
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) => {
|
|
88
162
|
if (err) {
|
|
89
163
|
this.hookFail({settings, statusCode, responseTime, err, data});
|
|
90
164
|
this.logError({settings, statusCode, responseTime, err, data});
|
|
91
165
|
this.emit('fail', err, data, settings, statusCode, responseTime);
|
|
92
166
|
} else {
|
|
93
|
-
this.hookSuccess({settings, statusCode, responseTime});
|
|
167
|
+
this.hookSuccess({data, settings, statusCode, responseTime});
|
|
94
168
|
this.logSuccess({settings, statusCode, responseTime});
|
|
95
169
|
this.emit('success', data, settings, statusCode, responseTime);
|
|
96
170
|
}
|
|
@@ -99,25 +173,27 @@ class RemoteModel extends LocalModel {
|
|
|
99
173
|
}
|
|
100
174
|
};
|
|
101
175
|
|
|
102
|
-
|
|
103
|
-
|
|
176
|
+
this.got(settings)
|
|
177
|
+
.catch(err => {
|
|
178
|
+
debug('request got error', err);
|
|
179
|
+
setResponseTime();
|
|
104
180
|
if (err.code === 'ETIMEDOUT') {
|
|
105
181
|
err.message = 'Connection timed out';
|
|
106
182
|
err.status = 504;
|
|
107
183
|
}
|
|
108
|
-
err.status = err.status || (response && response.statusCode) || 503;
|
|
109
|
-
return _callback(err, null, err.status);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
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
|
+
});
|
|
117
192
|
}
|
|
118
193
|
|
|
119
194
|
handleResponse(response, callback) {
|
|
120
|
-
|
|
195
|
+
debug('handleResponse', response);
|
|
196
|
+
let data;
|
|
121
197
|
try {
|
|
122
198
|
data = JSON.parse(response.body || '{}');
|
|
123
199
|
} catch (err) {
|
|
@@ -129,53 +205,37 @@ class RemoteModel extends LocalModel {
|
|
|
129
205
|
}
|
|
130
206
|
|
|
131
207
|
parseResponse(statusCode, data, callback) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
callback(err, null, statusCode);
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
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);
|
|
141
213
|
}
|
|
142
|
-
}
|
|
143
214
|
|
|
144
|
-
|
|
145
|
-
|
|
215
|
+
try {
|
|
216
|
+
data = this.parse(data);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return callback(err, null, statusCode);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
callback(null, data, statusCode);
|
|
146
222
|
}
|
|
147
223
|
|
|
148
224
|
parse(data) {
|
|
225
|
+
debug('parse', data);
|
|
226
|
+
if (data && typeof data === 'object') this.set(data);
|
|
149
227
|
return data;
|
|
150
228
|
}
|
|
151
229
|
|
|
152
230
|
parseError(statusCode, data) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
url() {
|
|
157
|
-
return this.options.url;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
auth(credentials) {
|
|
161
|
-
if (!credentials) return;
|
|
162
|
-
|
|
163
|
-
if (typeof credentials === 'string') {
|
|
164
|
-
let auth = credentials.split(':');
|
|
165
|
-
credentials = {
|
|
166
|
-
user: auth.shift(),
|
|
167
|
-
pass: auth.join(':'),
|
|
168
|
-
sendImmediately: true
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return credentials;
|
|
231
|
+
debug('parseError, statusCode, data');
|
|
232
|
+
return Object.assign({ status: statusCode }, data);
|
|
173
233
|
}
|
|
174
234
|
|
|
175
235
|
logMeta(tokenData) {
|
|
176
236
|
let data = {
|
|
177
237
|
outVerb: tokenData.settings.method,
|
|
178
|
-
outRequest: tokenData.settings.
|
|
238
|
+
outRequest: tokenData.settings.url
|
|
179
239
|
};
|
|
180
240
|
|
|
181
241
|
if (tokenData.statusCode) {
|
|
@@ -193,7 +253,7 @@ class RemoteModel extends LocalModel {
|
|
|
193
253
|
data.outErrorBody = this.logger.trimHtml(tokenData.err.body);
|
|
194
254
|
}
|
|
195
255
|
|
|
196
|
-
|
|
256
|
+
Object.assign(data, this.options.logging);
|
|
197
257
|
|
|
198
258
|
return data;
|
|
199
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
|
-
"
|
|
28
|
+
"debug": "^4.3.3",
|
|
29
|
+
"got": "^11.8.3",
|
|
30
|
+
"hpagent": "^0.1.2",
|
|
29
31
|
"lodash.kebabcase": "^4.1.1",
|
|
30
|
-
"
|
|
31
|
-
"underscore": "^1.12.0"
|
|
32
|
+
"underscore": "^1.13.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"chai": "^4.3.
|
|
35
|
-
"eslint": "^
|
|
36
|
-
"
|
|
35
|
+
"chai": "^4.3.4",
|
|
36
|
+
"eslint": "^8.3.0",
|
|
37
|
+
"hmpo-logger": "^4.1.3",
|
|
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,39 +26,60 @@ 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);
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
describe('constructor', () => {
|
|
35
|
+
let Model;
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
Model = require('../../lib/remote-model');
|
|
37
39
|
sinon.stub(Model.prototype, 'setLogger');
|
|
40
|
+
});
|
|
38
41
|
|
|
42
|
+
it('should call setLogger', () => {
|
|
39
43
|
model = new Model();
|
|
40
|
-
|
|
41
44
|
model.setLogger.should.have.been.calledWithExactly();
|
|
42
45
|
});
|
|
43
46
|
|
|
47
|
+
it('should set model label name', () => {
|
|
48
|
+
model = new Model();
|
|
49
|
+
model.options.label.should.equal('remote-model');
|
|
50
|
+
});
|
|
51
|
+
|
|
44
52
|
afterEach(() => {
|
|
45
53
|
Model.prototype.setLogger.restore();
|
|
46
54
|
});
|
|
47
55
|
});
|
|
48
56
|
|
|
49
57
|
describe('setLogger', () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'hmpo-logger': {
|
|
53
|
-
get: getStub
|
|
54
|
-
}
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
sinon.stub(logger, 'get').returns('logger');
|
|
55
60
|
});
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
logger.get.restore();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should set up a new hmpo-logger', () => {
|
|
67
|
+
model = new Model();
|
|
68
|
+
|
|
69
|
+
logger.get.should.have.been.calledWithExactly(':remote-model');
|
|
70
|
+
model.logger.should.equal('logger');
|
|
71
|
+
});
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
it('should use console log and a trimHtml pass-through if hmpo-logger is not available', () => {
|
|
74
|
+
logger.get.throws(new Error());
|
|
75
|
+
|
|
76
|
+
model = new Model();
|
|
77
|
+
|
|
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);
|
|
82
|
+
});
|
|
60
83
|
});
|
|
61
84
|
|
|
62
85
|
describe('fetch', () => {
|
|
@@ -77,6 +100,15 @@ describe('Remote Model', () => {
|
|
|
77
100
|
method: 'GET'
|
|
78
101
|
});
|
|
79
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
|
+
|
|
80
112
|
it('should call request', () => {
|
|
81
113
|
model.requestConfig.returns(mocks.config);
|
|
82
114
|
|
|
@@ -102,9 +134,9 @@ describe('Remote Model', () => {
|
|
|
102
134
|
it('should call prepare', () => {
|
|
103
135
|
model.save(cb);
|
|
104
136
|
|
|
105
|
-
model.prepare.should.have.been.
|
|
106
|
-
|
|
137
|
+
model.prepare.should.have.been.calledWithExactly(sinon.match.func);
|
|
107
138
|
});
|
|
139
|
+
|
|
108
140
|
it('should call callback with an error', () => {
|
|
109
141
|
let error = new Error('error');
|
|
110
142
|
model.prepare.yields(error);
|
|
@@ -112,7 +144,6 @@ describe('Remote Model', () => {
|
|
|
112
144
|
model.save(cb);
|
|
113
145
|
|
|
114
146
|
cb.should.have.been.calledWith(error);
|
|
115
|
-
|
|
116
147
|
});
|
|
117
148
|
|
|
118
149
|
context('on prepare success', () => {
|
|
@@ -128,20 +159,27 @@ describe('Remote Model', () => {
|
|
|
128
159
|
it('should use requestConfig', () => {
|
|
129
160
|
model.save(cb);
|
|
130
161
|
|
|
131
|
-
model.requestConfig.should.have.been.
|
|
162
|
+
model.requestConfig.should.have.been.calledWithExactly({
|
|
132
163
|
method: 'POST',
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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' });
|
|
138
175
|
});
|
|
176
|
+
|
|
139
177
|
it('should call request', () => {
|
|
140
178
|
model.requestConfig.returns(mocks.config);
|
|
141
179
|
|
|
142
180
|
model.save(cb);
|
|
143
181
|
|
|
144
|
-
model.request.should.have.been.calledWith(mocks.config,
|
|
182
|
+
model.request.should.have.been.calledWith(mocks.config, cb);
|
|
145
183
|
});
|
|
146
184
|
});
|
|
147
185
|
|
|
@@ -165,6 +203,15 @@ describe('Remote Model', () => {
|
|
|
165
203
|
method: 'DELETE'
|
|
166
204
|
});
|
|
167
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
|
+
|
|
168
215
|
it('should call request', () => {
|
|
169
216
|
model.requestConfig.returns(mocks.config);
|
|
170
217
|
|
|
@@ -175,50 +222,227 @@ describe('Remote Model', () => {
|
|
|
175
222
|
});
|
|
176
223
|
|
|
177
224
|
describe('requestConfig', () => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
+
});
|
|
181
231
|
|
|
182
|
-
|
|
183
|
-
|
|
232
|
+
config.url.should.equal('https://example.com/options');
|
|
233
|
+
});
|
|
184
234
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
});
|
|
188
265
|
});
|
|
189
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
|
+
});
|
|
190
273
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
'
|
|
274
|
+
config.username.should.equal('options');
|
|
275
|
+
config.password.should.equal('pass:word');
|
|
276
|
+
config.should.not.have.property('auth');
|
|
277
|
+
});
|
|
278
|
+
|
|
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');
|
|
194
289
|
});
|
|
195
290
|
|
|
196
|
-
|
|
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
|
+
});
|
|
197
304
|
});
|
|
198
305
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
});
|
|
202
340
|
});
|
|
203
341
|
|
|
204
|
-
|
|
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
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
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
|
+
});
|
|
205
388
|
});
|
|
206
389
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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');
|
|
211
399
|
});
|
|
212
400
|
|
|
213
|
-
|
|
214
|
-
|
|
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
|
+
});
|
|
215
413
|
});
|
|
216
414
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
+
});
|
|
222
446
|
});
|
|
223
447
|
});
|
|
224
448
|
|
|
@@ -330,14 +554,15 @@ describe('Remote Model', () => {
|
|
|
330
554
|
});
|
|
331
555
|
|
|
332
556
|
describe('request', () => {
|
|
333
|
-
let settings,
|
|
557
|
+
let settings, requestSettings, mocks;
|
|
334
558
|
|
|
335
559
|
beforeEach(() => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
560
|
+
mocks = {};
|
|
561
|
+
mocks.got = sinon.stub().returns(mocks);
|
|
562
|
+
mocks.then = sinon.stub().returns(mocks);
|
|
563
|
+
mocks.catch = sinon.stub().returns(mocks);
|
|
340
564
|
model = new Model();
|
|
565
|
+
model.got = mocks.got;
|
|
341
566
|
|
|
342
567
|
sinon.stub(model, 'logSync');
|
|
343
568
|
sinon.stub(model, 'logSuccess');
|
|
@@ -358,22 +583,11 @@ describe('Remote Model', () => {
|
|
|
358
583
|
model.emit.restore();
|
|
359
584
|
});
|
|
360
585
|
|
|
361
|
-
it('should invoke request with settings including a body', () => {
|
|
362
|
-
requestSettings.body = body;
|
|
363
|
-
|
|
364
|
-
model.request(settings, body, cb);
|
|
365
|
-
|
|
366
|
-
mocks.request.should.have.been.called;
|
|
367
|
-
mocks.request.should.have.been.calledWith(requestSettings);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
586
|
it('should invoke request with request settings', () => {
|
|
371
|
-
requestSettings.body = undefined;
|
|
372
|
-
|
|
373
587
|
model.request(settings, cb);
|
|
374
588
|
|
|
375
|
-
mocks.
|
|
376
|
-
mocks.
|
|
589
|
+
mocks.got.should.have.been.called;
|
|
590
|
+
mocks.got.should.have.been.calledWith(requestSettings);
|
|
377
591
|
});
|
|
378
592
|
|
|
379
593
|
it('should log sync messages', () => {
|
|
@@ -405,7 +619,7 @@ describe('Remote Model', () => {
|
|
|
405
619
|
});
|
|
406
620
|
|
|
407
621
|
it('should work without a callback', () => {
|
|
408
|
-
mocks.
|
|
622
|
+
mocks.catch.yields(new Error('Random Error'));
|
|
409
623
|
|
|
410
624
|
model.request(settings);
|
|
411
625
|
|
|
@@ -424,7 +638,9 @@ describe('Remote Model', () => {
|
|
|
424
638
|
'statusCode': 418
|
|
425
639
|
};
|
|
426
640
|
|
|
427
|
-
|
|
641
|
+
error.response = response;
|
|
642
|
+
|
|
643
|
+
mocks.catch.yields(error);
|
|
428
644
|
});
|
|
429
645
|
|
|
430
646
|
it('should log error messages', () => {
|
|
@@ -507,7 +723,7 @@ describe('Remote Model', () => {
|
|
|
507
723
|
|
|
508
724
|
context('on success', () => {
|
|
509
725
|
beforeEach(() => {
|
|
510
|
-
mocks.
|
|
726
|
+
mocks.then.yields({
|
|
511
727
|
'body': JSON.stringify({'data': 'value'}),
|
|
512
728
|
'statusCode': 200
|
|
513
729
|
});
|
|
@@ -541,6 +757,7 @@ describe('Remote Model', () => {
|
|
|
541
757
|
model.request(settings, cb);
|
|
542
758
|
|
|
543
759
|
hook.should.have.been.calledWithExactly({
|
|
760
|
+
data: {'data': 'value'},
|
|
544
761
|
statusCode: 200,
|
|
545
762
|
settings: settings,
|
|
546
763
|
responseTime: sinon.match.number
|
|
@@ -613,6 +830,7 @@ describe('Remote Model', () => {
|
|
|
613
830
|
|
|
614
831
|
afterEach(() => {
|
|
615
832
|
model.parse.restore();
|
|
833
|
+
|
|
616
834
|
model.parseError.restore();
|
|
617
835
|
});
|
|
618
836
|
|
|
@@ -686,6 +904,17 @@ describe('Remote Model', () => {
|
|
|
686
904
|
it('returns data passed', () => {
|
|
687
905
|
model.parse({ data: 1 }).should.eql({ data: 1 });
|
|
688
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
|
+
});
|
|
689
918
|
});
|
|
690
919
|
|
|
691
920
|
describe('parseError', () => {
|
|
@@ -715,12 +944,11 @@ describe('Remote Model', () => {
|
|
|
715
944
|
});
|
|
716
945
|
|
|
717
946
|
it('should return parsed credentials if credentials is a string', () => {
|
|
718
|
-
let credentials = model.auth('username:
|
|
947
|
+
let credentials = model.auth('username:pass:word');
|
|
719
948
|
|
|
720
949
|
credentials.should.deep.equal({
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
sendImmediately: true
|
|
950
|
+
username: 'username',
|
|
951
|
+
password: 'pass:word'
|
|
724
952
|
});
|
|
725
953
|
|
|
726
954
|
});
|
|
@@ -739,21 +967,21 @@ describe('Remote Model', () => {
|
|
|
739
967
|
|
|
740
968
|
|
|
741
969
|
describe('logging', () => {
|
|
742
|
-
let
|
|
970
|
+
let mocks;
|
|
743
971
|
|
|
744
972
|
beforeEach(() => {
|
|
745
|
-
|
|
973
|
+
mocks = {
|
|
746
974
|
outbound: sinon.stub(),
|
|
747
975
|
trimHtml: sinon.stub()
|
|
748
976
|
};
|
|
749
|
-
|
|
750
|
-
let Model = proxyquire('../../lib/remote-model', {
|
|
751
|
-
'hmpo-logger': {
|
|
752
|
-
get: () => logger,
|
|
753
|
-
}
|
|
754
|
-
});
|
|
977
|
+
sinon.stub(logger, 'get').returns(mocks);
|
|
755
978
|
|
|
756
979
|
model = new Model();
|
|
980
|
+
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
afterEach(() => {
|
|
984
|
+
logger.get.restore();
|
|
757
985
|
});
|
|
758
986
|
|
|
759
987
|
describe('logSync', () => {
|
|
@@ -761,7 +989,7 @@ describe('Remote Model', () => {
|
|
|
761
989
|
let args = {
|
|
762
990
|
settings: {
|
|
763
991
|
method: 'VERB',
|
|
764
|
-
|
|
992
|
+
url: 'http://example.org'
|
|
765
993
|
}
|
|
766
994
|
};
|
|
767
995
|
|
|
@@ -772,7 +1000,7 @@ describe('Remote Model', () => {
|
|
|
772
1000
|
|
|
773
1001
|
model.logSync(args);
|
|
774
1002
|
|
|
775
|
-
|
|
1003
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
776
1004
|
'Model request sent :outVerb :outRequest',
|
|
777
1005
|
argsAsMeta
|
|
778
1006
|
);
|
|
@@ -784,7 +1012,7 @@ describe('Remote Model', () => {
|
|
|
784
1012
|
let args = {
|
|
785
1013
|
settings: {
|
|
786
1014
|
method: 'VERB',
|
|
787
|
-
|
|
1015
|
+
url: 'http://example.org'
|
|
788
1016
|
},
|
|
789
1017
|
statusCode: 418,
|
|
790
1018
|
responseTime: 1000
|
|
@@ -799,7 +1027,7 @@ describe('Remote Model', () => {
|
|
|
799
1027
|
|
|
800
1028
|
model.logError(args);
|
|
801
1029
|
|
|
802
|
-
|
|
1030
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
803
1031
|
'Model request failed :outVerb :outRequest :outResponseCode :outError',
|
|
804
1032
|
argsAsMeta
|
|
805
1033
|
);
|
|
@@ -811,7 +1039,7 @@ describe('Remote Model', () => {
|
|
|
811
1039
|
let args = {
|
|
812
1040
|
settings: {
|
|
813
1041
|
method: 'VERB',
|
|
814
|
-
|
|
1042
|
+
url: 'http://example.org'
|
|
815
1043
|
},
|
|
816
1044
|
statusCode: 418,
|
|
817
1045
|
responseTime: 1000
|
|
@@ -826,7 +1054,7 @@ describe('Remote Model', () => {
|
|
|
826
1054
|
|
|
827
1055
|
model.logSuccess(args);
|
|
828
1056
|
|
|
829
|
-
|
|
1057
|
+
mocks.outbound.should.have.been.calledWithExactly(
|
|
830
1058
|
'Model request success :outVerb :outRequest :outResponseCode',
|
|
831
1059
|
argsAsMeta
|
|
832
1060
|
);
|
|
@@ -841,7 +1069,7 @@ describe('Remote Model', () => {
|
|
|
841
1069
|
data = {
|
|
842
1070
|
settings: {
|
|
843
1071
|
method: 'VERB',
|
|
844
|
-
|
|
1072
|
+
url: 'http://example.org'
|
|
845
1073
|
},
|
|
846
1074
|
statusCode: 418,
|
|
847
1075
|
responseTime: 3000,
|
|
@@ -916,7 +1144,7 @@ describe('Remote Model', () => {
|
|
|
916
1144
|
});
|
|
917
1145
|
|
|
918
1146
|
it('should be present with error', () => {
|
|
919
|
-
|
|
1147
|
+
mocks.trimHtml.returns('Html Body');
|
|
920
1148
|
|
|
921
1149
|
let result = model.logMeta(data);
|
|
922
1150
|
|