hmpo-model 3.2.0 → 4.0.1
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 +38 -42
- package/lib/remote-model.js +158 -93
- package/package.json +11 -10
- package/test/lib/spec.local-model.js +36 -18
- package/test/lib/spec.remote-model.js +316 -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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require('underscore');
|
|
4
3
|
const EventEmitter = require('events').EventEmitter;
|
|
5
4
|
|
|
6
5
|
class LocalModel extends EventEmitter {
|
|
@@ -9,8 +8,8 @@ class LocalModel extends EventEmitter {
|
|
|
9
8
|
super();
|
|
10
9
|
|
|
11
10
|
this.options = options || {};
|
|
12
|
-
this.attributes =
|
|
13
|
-
this.set(attributes, {silent: true});
|
|
11
|
+
this.attributes = Object.create(null);
|
|
12
|
+
if (attributes) this.set(attributes, {silent: true});
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
get(key) {
|
|
@@ -19,32 +18,35 @@ class LocalModel extends EventEmitter {
|
|
|
19
18
|
|
|
20
19
|
set(key, value, options) {
|
|
21
20
|
|
|
22
|
-
let attrs =
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
let attrs = Object.create(null);
|
|
22
|
+
if (key instanceof Map) {
|
|
23
|
+
Object.assign(attrs, Object.fromEntries(key));
|
|
24
|
+
options = value;
|
|
25
|
+
} else if (typeof key === 'string') {
|
|
25
26
|
attrs[key] = value;
|
|
26
27
|
} else {
|
|
27
|
-
attrs
|
|
28
|
+
Object.assign(attrs, key);
|
|
28
29
|
options = value;
|
|
29
30
|
}
|
|
30
|
-
options = options || {};
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// silent set
|
|
33
|
+
if (options && options.silent) {
|
|
34
|
+
Object.assign(this.attributes, attrs);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const changes = [];
|
|
39
|
+
for (let key in attrs) {
|
|
40
|
+
const value = attrs[key];
|
|
41
|
+
const old = this.attributes[key];
|
|
42
|
+
if (value !== old) changes.push([key, value, old]);
|
|
43
|
+
}
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
Object.assign(this.attributes, attrs);
|
|
42
46
|
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
this.emit('change', changed);
|
|
47
|
+
if (changes.length) {
|
|
48
|
+
changes.forEach(([key, value, old]) => this.emit('change:' + key, value, old));
|
|
49
|
+
if (this.listenerCount('change')) this.emit('change', Object.fromEntries(changes));
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
return this;
|
|
@@ -52,38 +54,32 @@ class LocalModel extends EventEmitter {
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
unset(fields, options) {
|
|
55
|
-
options = options || {};
|
|
56
57
|
if (typeof fields === 'string') {
|
|
57
58
|
fields = [fields];
|
|
58
59
|
}
|
|
59
|
-
let old = this.toJSON(),
|
|
60
|
-
changed = {};
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
const changes = [];
|
|
62
|
+
for (let key of fields) {
|
|
63
|
+
const old = this.attributes[key];
|
|
64
|
+
if (old !== undefined) {
|
|
65
|
+
changes.push([key, undefined, old]);
|
|
65
66
|
delete this.attributes[key];
|
|
66
67
|
}
|
|
67
|
-
}
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
if (!options.silent &&
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
this.emit('change', changed);
|
|
70
|
+
if ((!options || !options.silent) && changes.length) {
|
|
71
|
+
changes.forEach(([key, value, old]) => this.emit('change:' + key, value, old));
|
|
72
|
+
if (this.listenerCount('change')) this.emit('change', Object.fromEntries(changes));
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
return this;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
reset(options) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
_.each(keys, (key) => {
|
|
85
|
-
this.emit('change:' + key, undefined);
|
|
86
|
-
});
|
|
79
|
+
const old = this.attributes;
|
|
80
|
+
this.attributes = Object.create(null);
|
|
81
|
+
if (!options || !options.silent) {
|
|
82
|
+
Object.keys(old).forEach(key => this.emit('change:' + key, undefined, old[key]));
|
|
87
83
|
this.emit('reset');
|
|
88
84
|
}
|
|
89
85
|
}
|
|
@@ -97,8 +93,8 @@ class LocalModel extends EventEmitter {
|
|
|
97
93
|
this.set(property, val + amount);
|
|
98
94
|
}
|
|
99
95
|
|
|
100
|
-
toJSON() {
|
|
101
|
-
return
|
|
96
|
+
toJSON(bare = false) {
|
|
97
|
+
return Object.assign(bare ? Object.create(null) : {}, this.attributes);
|
|
102
98
|
}
|
|
103
99
|
}
|
|
104
100
|
|
package/lib/remote-model.js
CHANGED
|
@@ -1,91 +1,164 @@
|
|
|
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;
|
|
13
15
|
this.options.label = this.options.label || kebabCase(this.constructor.name);
|
|
14
16
|
this.setLogger();
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
setLogger() {
|
|
18
|
-
|
|
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
|
+
}
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
fetch(callback) {
|
|
22
|
-
|
|
29
|
+
fetch(args, callback) {
|
|
30
|
+
if (typeof args === 'function') {
|
|
31
|
+
callback = args;
|
|
32
|
+
args = undefined;
|
|
33
|
+
}
|
|
34
|
+
const config = this.requestConfig({method: 'GET'}, args);
|
|
23
35
|
this.request(config, callback);
|
|
24
36
|
}
|
|
25
37
|
|
|
26
|
-
save(callback) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
headers: {
|
|
36
|
-
'Content-Type': 'application/json',
|
|
37
|
-
'Content-Length': Buffer.byteLength(data)
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
this.request(config, data, callback);
|
|
42
|
-
|
|
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);
|
|
43
47
|
});
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
delete(callback) {
|
|
47
|
-
|
|
50
|
+
delete(args, callback) {
|
|
51
|
+
if (typeof args === 'function') {
|
|
52
|
+
callback = args;
|
|
53
|
+
args = undefined;
|
|
54
|
+
}
|
|
55
|
+
const config = this.requestConfig({method: 'DELETE'}, args);
|
|
48
56
|
this.request(config, callback);
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
prepare(callback) {
|
|
60
|
+
debug('prepare');
|
|
61
|
+
callback(null, this.toJSON());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
requestConfig(config, args) {
|
|
65
|
+
const retConfig = Object.assign({}, config);
|
|
53
66
|
|
|
54
|
-
retConfig.
|
|
67
|
+
retConfig.url = this.url(retConfig.url || retConfig.uri, args);
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
retConfig.timeout = this.timeout(retConfig.timeout);
|
|
57
70
|
|
|
71
|
+
const auth = this.auth(retConfig.auth);
|
|
58
72
|
if (auth) {
|
|
59
|
-
retConfig.
|
|
73
|
+
retConfig.username = auth.username || auth.user;
|
|
74
|
+
retConfig.password = auth.password || auth.pass;
|
|
60
75
|
}
|
|
76
|
+
delete retConfig.auth;
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
const agent = this.proxy(retConfig.proxy, retConfig.url);
|
|
79
|
+
if (agent) {
|
|
80
|
+
retConfig.agent = agent;
|
|
64
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);
|
|
65
88
|
|
|
66
89
|
return retConfig;
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
};
|
|
73
103
|
}
|
|
104
|
+
return auth;
|
|
105
|
+
}
|
|
74
106
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return timeout;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
proxy(proxy = this.options.proxy, url) {
|
|
122
|
+
if (!proxy || !url) return;
|
|
123
|
+
|
|
124
|
+
if (typeof proxy === 'string') proxy = { proxy };
|
|
125
|
+
|
|
126
|
+
const isHttps = (new URL(url).protocol === 'https:');
|
|
79
127
|
|
|
80
|
-
|
|
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
|
+
}
|
|
81
148
|
|
|
82
|
-
|
|
149
|
+
request(settings, callback) {
|
|
150
|
+
this.hookSync({settings});
|
|
151
|
+
this.logSync({settings});
|
|
152
|
+
this.emit('sync', settings);
|
|
83
153
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
};
|
|
88
160
|
|
|
161
|
+
const _callback = (err, data, statusCode) => {
|
|
89
162
|
if (err) {
|
|
90
163
|
this.hookFail({settings, statusCode, responseTime, err, data});
|
|
91
164
|
this.logError({settings, statusCode, responseTime, err, data});
|
|
@@ -100,25 +173,27 @@ class RemoteModel extends LocalModel {
|
|
|
100
173
|
}
|
|
101
174
|
};
|
|
102
175
|
|
|
103
|
-
|
|
104
|
-
|
|
176
|
+
this.got(settings)
|
|
177
|
+
.catch(err => {
|
|
178
|
+
debug('request got error', err);
|
|
179
|
+
setResponseTime();
|
|
105
180
|
if (err.code === 'ETIMEDOUT') {
|
|
106
181
|
err.message = 'Connection timed out';
|
|
107
182
|
err.status = 504;
|
|
108
183
|
}
|
|
109
|
-
err.status = err.status || (response && response.statusCode) || 503;
|
|
110
|
-
return _callback(err, null, err.status);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
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
|
+
});
|
|
118
192
|
}
|
|
119
193
|
|
|
120
194
|
handleResponse(response, callback) {
|
|
121
|
-
|
|
195
|
+
debug('handleResponse', response);
|
|
196
|
+
let data;
|
|
122
197
|
try {
|
|
123
198
|
data = JSON.parse(response.body || '{}');
|
|
124
199
|
} catch (err) {
|
|
@@ -130,53 +205,43 @@ class RemoteModel extends LocalModel {
|
|
|
130
205
|
}
|
|
131
206
|
|
|
132
207
|
parseResponse(statusCode, data, callback) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
callback(err, null, statusCode);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
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);
|
|
142
213
|
}
|
|
143
|
-
}
|
|
144
214
|
|
|
145
|
-
|
|
146
|
-
|
|
215
|
+
try {
|
|
216
|
+
data = this.parse(data);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return callback(err, null, statusCode);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
callback(null, data, statusCode);
|
|
147
222
|
}
|
|
148
223
|
|
|
149
224
|
parse(data) {
|
|
225
|
+
debug('parse', data);
|
|
226
|
+
if (data && typeof data === 'object') {
|
|
227
|
+
if (Array.isArray(data)) {
|
|
228
|
+
this.set('data', data);
|
|
229
|
+
} else {
|
|
230
|
+
this.set(data);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
150
233
|
return data;
|
|
151
234
|
}
|
|
152
235
|
|
|
153
236
|
parseError(statusCode, data) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
url() {
|
|
158
|
-
return this.options.url;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
auth(credentials) {
|
|
162
|
-
if (!credentials) return;
|
|
163
|
-
|
|
164
|
-
if (typeof credentials === 'string') {
|
|
165
|
-
let auth = credentials.split(':');
|
|
166
|
-
credentials = {
|
|
167
|
-
user: auth.shift(),
|
|
168
|
-
pass: auth.join(':'),
|
|
169
|
-
sendImmediately: true
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return credentials;
|
|
237
|
+
debug('parseError, statusCode, data');
|
|
238
|
+
return Object.assign({ status: statusCode }, data);
|
|
174
239
|
}
|
|
175
240
|
|
|
176
241
|
logMeta(tokenData) {
|
|
177
242
|
let data = {
|
|
178
243
|
outVerb: tokenData.settings.method,
|
|
179
|
-
outRequest: tokenData.settings.
|
|
244
|
+
outRequest: tokenData.settings.url
|
|
180
245
|
};
|
|
181
246
|
|
|
182
247
|
if (tokenData.statusCode) {
|
|
@@ -194,7 +259,7 @@ class RemoteModel extends LocalModel {
|
|
|
194
259
|
data.outErrorBody = this.logger.trimHtml(tokenData.err.body);
|
|
195
260
|
}
|
|
196
261
|
|
|
197
|
-
|
|
262
|
+
Object.assign(data, this.options.logging);
|
|
198
263
|
|
|
199
264
|
return data;
|
|
200
265
|
}
|