hmpo-model 4.0.0 → 4.0.3
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/.github/workflows/ci.yaml +27 -0
- package/lib/local-model.js +38 -42
- package/lib/model-error.js +51 -0
- package/lib/remote-model.js +18 -12
- package/package.json +7 -8
- package/test/lib/spec.local-model.js +36 -18
- package/test/lib/spec.remote-model.js +68 -16
- package/.circleci/config.yml +0 -21
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Node.js CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
node-version: [12.x, 14.x, 15.x]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v2
|
|
20
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
+
uses: actions/setup-node@v2
|
|
22
|
+
with:
|
|
23
|
+
node-version: ${{ matrix.node-version }}
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: npm ci
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: npm test
|
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 Object.assign({}, this.attributes);
|
|
96
|
+
toJSON(bare = false) {
|
|
97
|
+
return Object.assign(bare ? Object.create(null) : {}, this.attributes);
|
|
102
98
|
}
|
|
103
99
|
}
|
|
104
100
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
class ModelError extends Error {
|
|
3
|
+
constructor(e) {
|
|
4
|
+
super(e);
|
|
5
|
+
Object.defineProperties(this, {
|
|
6
|
+
original: {
|
|
7
|
+
value: e,
|
|
8
|
+
enumerable: false,
|
|
9
|
+
writable: false,
|
|
10
|
+
configurable: true,
|
|
11
|
+
},
|
|
12
|
+
name: {
|
|
13
|
+
value: e.name,
|
|
14
|
+
enumerable: false,
|
|
15
|
+
writable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
},
|
|
18
|
+
code: {
|
|
19
|
+
value: e.code,
|
|
20
|
+
enumerable: false,
|
|
21
|
+
writable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
},
|
|
24
|
+
errno: {
|
|
25
|
+
value: e.errno,
|
|
26
|
+
enumerable: false,
|
|
27
|
+
writable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
},
|
|
30
|
+
info: {
|
|
31
|
+
get: () => this.original.info,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
configurable: true
|
|
34
|
+
},
|
|
35
|
+
stack: {
|
|
36
|
+
get: () => this.original.stack,
|
|
37
|
+
enumerable: false,
|
|
38
|
+
configurable: true
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
this.code = e.code;
|
|
42
|
+
|
|
43
|
+
if (this.code === 'ETIMEDOUT') {
|
|
44
|
+
this.message = 'Connection timed out';
|
|
45
|
+
this.status = 504;
|
|
46
|
+
}
|
|
47
|
+
this.status = this.status || (e.response && e.response.statusCode) || 503;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = ModelError;
|
package/lib/remote-model.js
CHANGED
|
@@ -5,6 +5,7 @@ const LocalModel = require('./local-model');
|
|
|
5
5
|
const got = require('got');
|
|
6
6
|
const kebabCase = require('lodash.kebabcase');
|
|
7
7
|
const { URL } = require('url');
|
|
8
|
+
const ModelError = require('./model-error');
|
|
8
9
|
|
|
9
10
|
const DEFAULT_TIMEOUT = 60000;
|
|
10
11
|
|
|
@@ -174,20 +175,19 @@ class RemoteModel extends LocalModel {
|
|
|
174
175
|
};
|
|
175
176
|
|
|
176
177
|
this.got(settings)
|
|
177
|
-
.catch(err => {
|
|
178
|
-
debug('request got error', err);
|
|
179
|
-
setResponseTime();
|
|
180
|
-
if (err.code === 'ETIMEDOUT') {
|
|
181
|
-
err.message = 'Connection timed out';
|
|
182
|
-
err.status = 504;
|
|
183
|
-
}
|
|
184
|
-
err.status = err.status || (err.response && err.response.statusCode) || 503;
|
|
185
|
-
return _callback(err, null, err.status, err.response);
|
|
186
|
-
})
|
|
187
178
|
.then(response => {
|
|
188
179
|
debug('request got response', response);
|
|
189
180
|
setResponseTime();
|
|
190
181
|
this.handleResponse(response, _callback);
|
|
182
|
+
})
|
|
183
|
+
.catch(err => {
|
|
184
|
+
setResponseTime();
|
|
185
|
+
if (err.code === 'ERR_NON_2XX_3XX_RESPONSE' && err.response) {
|
|
186
|
+
return this.handleResponse(err.response, _callback);
|
|
187
|
+
}
|
|
188
|
+
err = new ModelError(err);
|
|
189
|
+
debug('request got error', err);
|
|
190
|
+
_callback(err, null, err.status);
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -199,7 +199,7 @@ class RemoteModel extends LocalModel {
|
|
|
199
199
|
} catch (err) {
|
|
200
200
|
err.status = response.statusCode;
|
|
201
201
|
err.body = response.body;
|
|
202
|
-
return callback(err, null,
|
|
202
|
+
return callback(err, null, err.status);
|
|
203
203
|
}
|
|
204
204
|
this.parseResponse(response.statusCode, data, callback);
|
|
205
205
|
}
|
|
@@ -223,7 +223,13 @@ class RemoteModel extends LocalModel {
|
|
|
223
223
|
|
|
224
224
|
parse(data) {
|
|
225
225
|
debug('parse', data);
|
|
226
|
-
if (data && typeof data === 'object')
|
|
226
|
+
if (data && typeof data === 'object') {
|
|
227
|
+
if (Array.isArray(data)) {
|
|
228
|
+
this.set('data', data);
|
|
229
|
+
} else {
|
|
230
|
+
this.set(data);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
227
233
|
return data;
|
|
228
234
|
}
|
|
229
235
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hmpo-model",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Simple model for interacting with http/rest apis.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -28,17 +28,16 @@
|
|
|
28
28
|
"debug": "^4.3.3",
|
|
29
29
|
"got": "^11.8.3",
|
|
30
30
|
"hpagent": "^0.1.2",
|
|
31
|
-
"lodash.kebabcase": "^4.1.1"
|
|
32
|
-
"underscore": "^1.13.1"
|
|
31
|
+
"lodash.kebabcase": "^4.1.1"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
|
-
"chai": "^4.3.
|
|
36
|
-
"eslint": "^8.
|
|
37
|
-
"hmpo-logger": "^4.1.
|
|
38
|
-
"mocha": "^9.1
|
|
34
|
+
"chai": "^4.3.6",
|
|
35
|
+
"eslint": "^8.10.0",
|
|
36
|
+
"hmpo-logger": "^4.1.4",
|
|
37
|
+
"mocha": "^9.2.1",
|
|
39
38
|
"nyc": "^15.1.0",
|
|
40
39
|
"proxyquire": "^2.0.0",
|
|
41
|
-
"sinon": "^
|
|
40
|
+
"sinon": "^13.0.1",
|
|
42
41
|
"sinon-chai": "^3.7.0"
|
|
43
42
|
},
|
|
44
43
|
"nyc": {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const Model = require('../../lib/local-model');
|
|
4
4
|
|
|
5
|
+
describe('Local Model', () => {
|
|
5
6
|
let model;
|
|
6
7
|
|
|
7
8
|
beforeEach(() => {
|
|
8
|
-
|
|
9
|
-
let Model = require('../../lib/local-model');
|
|
10
|
-
|
|
11
9
|
model = new Model();
|
|
12
10
|
});
|
|
13
11
|
|
|
@@ -18,7 +16,7 @@ describe('Local Model', () => {
|
|
|
18
16
|
});
|
|
19
17
|
|
|
20
18
|
it('has an attributes property of type object', () => {
|
|
21
|
-
model.attributes.
|
|
19
|
+
expect(model.attributes).to.be.an('object');
|
|
22
20
|
});
|
|
23
21
|
|
|
24
22
|
describe('constructor', () => {
|
|
@@ -54,20 +52,28 @@ describe('Local Model', () => {
|
|
|
54
52
|
describe('set', () => {
|
|
55
53
|
|
|
56
54
|
beforeEach(() => {
|
|
57
|
-
model
|
|
58
|
-
name: 'Test name'
|
|
59
|
-
};
|
|
55
|
+
model = new Model({ name: 'Test name' });
|
|
60
56
|
});
|
|
61
57
|
|
|
62
58
|
it('adds a key to the model attributes if the key is a string', () => {
|
|
63
|
-
model.set('age', 20)
|
|
59
|
+
model.set('age', 20);
|
|
60
|
+
expect(model.attributes).to.eql({
|
|
64
61
|
name: 'Test name',
|
|
65
62
|
age: 20
|
|
66
63
|
});
|
|
67
64
|
});
|
|
68
65
|
|
|
69
66
|
it('accepts an object as the key', () => {
|
|
70
|
-
model.set( { placeOfBirth: 'London' } )
|
|
67
|
+
model.set( { placeOfBirth: 'London' } );
|
|
68
|
+
expect(model.attributes).to.eql({
|
|
69
|
+
name: 'Test name',
|
|
70
|
+
placeOfBirth: 'London'
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('accepts a Map as the key', () => {
|
|
75
|
+
model.set( new Map([['placeOfBirth', 'London']]));
|
|
76
|
+
expect(model.attributes).to.eql({
|
|
71
77
|
name: 'Test name',
|
|
72
78
|
placeOfBirth: 'London'
|
|
73
79
|
});
|
|
@@ -141,17 +147,17 @@ describe('Local Model', () => {
|
|
|
141
147
|
|
|
142
148
|
it('removes properties from model when passed a string', () => {
|
|
143
149
|
model.unset('a');
|
|
144
|
-
model.toJSON().
|
|
150
|
+
expect(model.toJSON()).to.eql({ b: 2, c: 3 });
|
|
145
151
|
});
|
|
146
152
|
|
|
147
153
|
it('removes properties from model when passed an array', () => {
|
|
148
154
|
model.unset(['a', 'b']);
|
|
149
|
-
model.toJSON().
|
|
155
|
+
expect(model.toJSON()).to.eql({ c: 3 });
|
|
150
156
|
});
|
|
151
157
|
|
|
152
158
|
it('does nothing if passed a property that does not exist', () => {
|
|
153
159
|
model.unset('foo');
|
|
154
|
-
model.toJSON().
|
|
160
|
+
expect(model.toJSON()).to.eql({ a: 1, b: 2, c: 3 });
|
|
155
161
|
});
|
|
156
162
|
|
|
157
163
|
it('emits a change event', () => {
|
|
@@ -225,7 +231,7 @@ describe('Local Model', () => {
|
|
|
225
231
|
|
|
226
232
|
it('clears model attributes', () => {
|
|
227
233
|
model.reset();
|
|
228
|
-
model.toJSON().
|
|
234
|
+
expect(model.toJSON()).to.eql({});
|
|
229
235
|
expect(model.get('name')).to.be.undefined;
|
|
230
236
|
expect(model.get('age')).to.be.undefined;
|
|
231
237
|
});
|
|
@@ -244,9 +250,9 @@ describe('Local Model', () => {
|
|
|
244
250
|
model.on('change:age', listener2);
|
|
245
251
|
model.reset();
|
|
246
252
|
listener1.should.have.been.calledOnce;
|
|
247
|
-
listener1.should.have.been.calledWithExactly(undefined);
|
|
253
|
+
listener1.should.have.been.calledWithExactly(undefined, 'John');
|
|
248
254
|
listener2.should.have.been.calledOnce;
|
|
249
|
-
listener2.should.have.been.calledWithExactly(undefined);
|
|
255
|
+
listener2.should.have.been.calledWithExactly(undefined, 30);
|
|
250
256
|
});
|
|
251
257
|
|
|
252
258
|
it('emits no events if called with silent: true', () => {
|
|
@@ -266,10 +272,22 @@ describe('Local Model', () => {
|
|
|
266
272
|
};
|
|
267
273
|
});
|
|
268
274
|
|
|
269
|
-
it('returns
|
|
270
|
-
model.toJSON()
|
|
275
|
+
it('returns a bare object that\'s the same as the attributes property', () => {
|
|
276
|
+
const result = model.toJSON(true);
|
|
277
|
+
expect(result).to.eql({
|
|
271
278
|
name: 'Test name'
|
|
272
279
|
});
|
|
280
|
+
|
|
281
|
+
expect(result.constructor).to.be.undefined;
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('returns an object that\'s the same as the attributes property with object prototype', () => {
|
|
285
|
+
const result = model.toJSON();
|
|
286
|
+
expect(result).to.eql({
|
|
287
|
+
name: 'Test name'
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
expect(result.constructor).to.be.a('function');
|
|
273
291
|
});
|
|
274
292
|
});
|
|
275
293
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Model = require('../../lib/remote-model');
|
|
4
|
+
const ModelError = require('../../lib/model-error');
|
|
4
5
|
const BaseModel = require('../../lib/local-model');
|
|
5
|
-
const _ = require('underscore');
|
|
6
6
|
const logger = require('hmpo-logger');
|
|
7
7
|
|
|
8
8
|
const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
|
|
@@ -543,7 +543,7 @@ describe('Remote Model', () => {
|
|
|
543
543
|
}
|
|
544
544
|
};
|
|
545
545
|
|
|
546
|
-
let savedConfig =
|
|
546
|
+
let savedConfig = Object.assign({}, config);
|
|
547
547
|
|
|
548
548
|
let returnedConfig = model.requestConfig(config);
|
|
549
549
|
|
|
@@ -573,7 +573,7 @@ describe('Remote Model', () => {
|
|
|
573
573
|
method: 'VERB'
|
|
574
574
|
};
|
|
575
575
|
|
|
576
|
-
requestSettings =
|
|
576
|
+
requestSettings = Object.assign({}, settings);
|
|
577
577
|
});
|
|
578
578
|
|
|
579
579
|
afterEach(() => {
|
|
@@ -650,9 +650,16 @@ describe('Remote Model', () => {
|
|
|
650
650
|
settings: settings,
|
|
651
651
|
statusCode: 418,
|
|
652
652
|
responseTime: sinon.match.number,
|
|
653
|
-
err:
|
|
653
|
+
err: sinon.match.instanceOf(ModelError),
|
|
654
654
|
data: null
|
|
655
655
|
});
|
|
656
|
+
model.logError.args[0][0].err.should.include({
|
|
657
|
+
name: 'Error',
|
|
658
|
+
message: 'Error: Lorem Ipsum',
|
|
659
|
+
status: 418,
|
|
660
|
+
info: undefined,
|
|
661
|
+
});
|
|
662
|
+
model.logError.args[0][0].err.stack.should.be.a('string');
|
|
656
663
|
});
|
|
657
664
|
|
|
658
665
|
it('should emit a fail event', () => {
|
|
@@ -660,15 +667,17 @@ describe('Remote Model', () => {
|
|
|
660
667
|
|
|
661
668
|
model.emit.should.have.been.calledWithExactly(
|
|
662
669
|
'fail',
|
|
663
|
-
sinon.match(
|
|
664
|
-
message: error.message,
|
|
665
|
-
status: 418
|
|
666
|
-
}),
|
|
670
|
+
sinon.match.instanceOf(ModelError),
|
|
667
671
|
null,
|
|
668
672
|
settings,
|
|
669
673
|
418,
|
|
670
674
|
sinon.match.number
|
|
671
675
|
);
|
|
676
|
+
model.emit.args[1][1].should.include({
|
|
677
|
+
name: 'Error',
|
|
678
|
+
message: 'Error: Lorem Ipsum',
|
|
679
|
+
status: 418,
|
|
680
|
+
});
|
|
672
681
|
});
|
|
673
682
|
|
|
674
683
|
it('should translate timeout errors with status codes', () => {
|
|
@@ -680,11 +689,14 @@ describe('Remote Model', () => {
|
|
|
680
689
|
settings: settings,
|
|
681
690
|
statusCode: 504,
|
|
682
691
|
responseTime: sinon.match.number,
|
|
683
|
-
err: sinon.match(
|
|
684
|
-
message: 'Connection timed out'
|
|
685
|
-
}),
|
|
692
|
+
err: sinon.match.instanceOf(ModelError),
|
|
686
693
|
data: null
|
|
687
694
|
});
|
|
695
|
+
model.logError.args[0][0].err.should.include({
|
|
696
|
+
name: 'Error',
|
|
697
|
+
message: 'Connection timed out',
|
|
698
|
+
status: 504
|
|
699
|
+
});
|
|
688
700
|
});
|
|
689
701
|
|
|
690
702
|
it('should translate errors without status codes', () => {
|
|
@@ -696,12 +708,14 @@ describe('Remote Model', () => {
|
|
|
696
708
|
settings: settings,
|
|
697
709
|
statusCode: 503,
|
|
698
710
|
responseTime: sinon.match.number,
|
|
699
|
-
err: sinon.match(
|
|
700
|
-
message: error.message,
|
|
701
|
-
status: 503,
|
|
702
|
-
}),
|
|
711
|
+
err: sinon.match.instanceOf(ModelError),
|
|
703
712
|
data: null
|
|
704
713
|
});
|
|
714
|
+
model.logError.args[0][0].err.should.include({
|
|
715
|
+
name: 'Error',
|
|
716
|
+
message: 'Error: Lorem Ipsum',
|
|
717
|
+
status: 503
|
|
718
|
+
});
|
|
705
719
|
});
|
|
706
720
|
|
|
707
721
|
it('should fire fail hook', () => {
|
|
@@ -714,11 +728,44 @@ describe('Remote Model', () => {
|
|
|
714
728
|
settings: settings,
|
|
715
729
|
statusCode: 418,
|
|
716
730
|
responseTime: sinon.match.number,
|
|
717
|
-
err:
|
|
731
|
+
err: sinon.match.instanceOf(ModelError),
|
|
718
732
|
data: null
|
|
719
733
|
});
|
|
720
734
|
hook.should.have.been.calledOn(model);
|
|
721
735
|
});
|
|
736
|
+
|
|
737
|
+
it('should handle response for ERR_NON_2XX_3XX_RESPONSE errors', () => {
|
|
738
|
+
error.code = 'ERR_NON_2XX_3XX_RESPONSE';
|
|
739
|
+
error.response = {
|
|
740
|
+
'body': JSON.stringify({'data': 'value'}),
|
|
741
|
+
'statusCode': 404
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
model.request(settings, cb);
|
|
745
|
+
console.log(model.logError.args[0][0]);
|
|
746
|
+
model.logError.should.have.been.calledWithExactly({
|
|
747
|
+
settings: settings,
|
|
748
|
+
statusCode: 404,
|
|
749
|
+
responseTime: sinon.match.number,
|
|
750
|
+
err: {
|
|
751
|
+
status: 404,
|
|
752
|
+
data: 'value'
|
|
753
|
+
},
|
|
754
|
+
data: { data: 'value' }
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
cb.should.have.been.calledWithExactly(
|
|
758
|
+
{
|
|
759
|
+
status: 404,
|
|
760
|
+
data: 'value'
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
data: 'value'
|
|
764
|
+
},
|
|
765
|
+
sinon.match.number
|
|
766
|
+
);
|
|
767
|
+
});
|
|
768
|
+
|
|
722
769
|
});
|
|
723
770
|
|
|
724
771
|
context('on success', () => {
|
|
@@ -910,6 +957,11 @@ describe('Remote Model', () => {
|
|
|
910
957
|
model.get('foo').should.equal('bar');
|
|
911
958
|
});
|
|
912
959
|
|
|
960
|
+
it('sets the parsed array data to the model as "data"', () => {
|
|
961
|
+
model.parse([1, 2, 3, 4]);
|
|
962
|
+
model.get('data').should.eql([1, 2, 3, 4]);
|
|
963
|
+
});
|
|
964
|
+
|
|
913
965
|
it('does not set if the data falsey', () => {
|
|
914
966
|
model.set = sinon.stub();
|
|
915
967
|
model.parse(null);
|
package/.circleci/config.yml
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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
|