particle-api-js 9.4.0 → 10.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 +7 -5
- package/.eslintrc.js +3 -3
- package/CHANGELOG.md +8 -0
- package/RELEASE.md +1 -1
- package/dist/particle.min.js +227 -192
- package/dist/particle.min.js.map +1 -1
- package/docs/api.md +561 -103
- package/fs.js +2 -0
- package/lib/Agent.js +320 -221
- package/lib/Agent.js.map +1 -1
- package/lib/Particle.js +645 -62
- package/lib/Particle.js.map +1 -1
- package/package.json +10 -10
- package/test/Agent.integration.js +3 -1
- package/test/Agent.spec.js +173 -289
- package/test/EventStream.spec.js +1 -1
- package/test/Particle.integration.js +5 -4
- package/test/Particle.spec.js +325 -11
- package/lib/superagent-binary-parser.js +0 -20
- package/lib/superagent-binary-parser.js.map +0 -1
- /package/{test/EventStream-e2e-browser.html → EventStream-e2e-browser.html} +0 -0
- /package/{test/EventStream-e2e-node.js → EventStream-e2e-node.js} +0 -0
package/test/Agent.spec.js
CHANGED
|
@@ -15,20 +15,12 @@ describe('Agent', () => {
|
|
|
15
15
|
expect(agent.setBaseUrl.firstCall.args).to.have.lengthOf(1);
|
|
16
16
|
expect(agent.setBaseUrl.firstCall.args[0]).to.eql(baseUrl);
|
|
17
17
|
});
|
|
18
|
-
|
|
19
|
-
it('creates a prefix function instance property from the supplied baseUrl (via setBaseUrl)', () => {
|
|
20
|
-
const agent = new Agent();
|
|
21
|
-
expect(agent.prefix).to.be.a('Function');
|
|
22
|
-
// Note: validating specific .prefix function behavior seems hard without proxyquire
|
|
23
|
-
// and this test seems sufficient
|
|
24
|
-
// and redundant with the 'build request uses prefix if provided' test below
|
|
25
|
-
});
|
|
26
18
|
});
|
|
27
19
|
|
|
28
20
|
describe('sanitize files', () => {
|
|
29
21
|
it('can call sanitize will falsy value', () => {
|
|
30
22
|
const agent = new Agent();
|
|
31
|
-
expect(agent._sanitizeFiles(undefined)).to.be.
|
|
23
|
+
expect(agent._sanitizeFiles(undefined)).to.be.undefined;
|
|
32
24
|
});
|
|
33
25
|
|
|
34
26
|
it('sanitizes file names', () => {
|
|
@@ -107,25 +99,37 @@ describe('Agent', () => {
|
|
|
107
99
|
});
|
|
108
100
|
|
|
109
101
|
it('authorize no auth is unchanged', () => {
|
|
110
|
-
expect(agent.
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('authorize with credentials', () => {
|
|
114
|
-
const authfn = sinon.spy();
|
|
115
|
-
const req = { auth: authfn };
|
|
116
|
-
const auth = { username: 'me', password: 'pwd' };
|
|
117
|
-
expect(agent._authorizationHeader(req, auth)).to.be.equal(req);
|
|
118
|
-
expect(authfn).to.have.been.calledWith('me', 'pwd');
|
|
102
|
+
expect(agent._getAuthorizationHeader(undefined)).to.eql({});
|
|
119
103
|
});
|
|
120
104
|
|
|
121
105
|
it('authorize with bearer', () => {
|
|
122
106
|
const auth = '123';
|
|
123
107
|
const bearer = 'Bearer 123';
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
expect(agent._authorizationHeader(req, auth)).to.be.equal(req);
|
|
127
|
-
expect(setfn).to.have.been.calledWith({ Authorization: bearer });
|
|
108
|
+
const headers = agent._getAuthorizationHeader(auth);
|
|
109
|
+
expect(headers).to.eql({ Authorization: bearer });
|
|
128
110
|
});
|
|
111
|
+
|
|
112
|
+
if (typeof window !== 'undefined') {
|
|
113
|
+
it('supports auth with user/pass in browsers', () => {
|
|
114
|
+
const auth = {
|
|
115
|
+
username: 'test@particle.io',
|
|
116
|
+
password: 'super_secret'
|
|
117
|
+
};
|
|
118
|
+
const basic = 'Basic dGVzdEBwYXJ0aWNsZS5pbzpzdXBlcl9zZWNyZXQ=';
|
|
119
|
+
const headers = agent._getAuthorizationHeader(auth);
|
|
120
|
+
expect(headers).to.eql({ Authorization: basic });
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
it('supports auth with user/pass in node', () => {
|
|
124
|
+
const auth = {
|
|
125
|
+
username: 'test@particle.io',
|
|
126
|
+
password: 'super_secret'
|
|
127
|
+
};
|
|
128
|
+
const basic = 'Basic dGVzdEBwYXJ0aWNsZS5pbzpzdXBlcl9zZWNyZXQ=';
|
|
129
|
+
const headers = agent._getAuthorizationHeader(auth);
|
|
130
|
+
expect(headers).to.eql({ Authorization: basic });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
129
133
|
});
|
|
130
134
|
|
|
131
135
|
describe('request', () => {
|
|
@@ -133,8 +137,9 @@ describe('Agent', () => {
|
|
|
133
137
|
|
|
134
138
|
beforeEach(() => {
|
|
135
139
|
agent = new Agent();
|
|
136
|
-
agent.
|
|
137
|
-
agent.
|
|
140
|
+
agent._promiseResponse = sinon.stub();
|
|
141
|
+
agent._promiseResponse.resolves('fake-response');
|
|
142
|
+
agent._buildRequest = sinon.stub();
|
|
138
143
|
agent._sanitizeFiles = sinon.stub();
|
|
139
144
|
});
|
|
140
145
|
|
|
@@ -148,37 +153,16 @@ describe('Agent', () => {
|
|
|
148
153
|
.then((res) => {
|
|
149
154
|
expect(res).to.be.equal('fake-response');
|
|
150
155
|
expect(agent._sanitizeFiles).calledOnce.calledWith(sinon.match.same(files));
|
|
151
|
-
expect(agent._request).calledOnce.calledWith({
|
|
152
|
-
uri: 'abc',
|
|
153
|
-
method: 'post',
|
|
154
|
-
auth: undefined,
|
|
155
|
-
headers: undefined,
|
|
156
|
-
query: 'all',
|
|
157
|
-
data: '123',
|
|
158
|
-
files: sanitizedFiles,
|
|
159
|
-
context: undefined,
|
|
160
|
-
raw: false,
|
|
161
|
-
form
|
|
162
|
-
});
|
|
163
156
|
});
|
|
164
157
|
});
|
|
165
158
|
|
|
166
159
|
it('uses default arguments for request', () => {
|
|
160
|
+
const args = ['abc', { args: '123' }];
|
|
161
|
+
agent._buildRequest.returns(args);
|
|
167
162
|
return agent.request({ uri: 'abc', method:'post' })
|
|
168
163
|
.then((res) => {
|
|
169
164
|
expect(res).to.be.equal('fake-response');
|
|
170
|
-
expect(agent.
|
|
171
|
-
uri: 'abc',
|
|
172
|
-
method:'post',
|
|
173
|
-
auth: undefined,
|
|
174
|
-
headers: undefined,
|
|
175
|
-
data: undefined,
|
|
176
|
-
files: undefined,
|
|
177
|
-
form: undefined,
|
|
178
|
-
query: undefined,
|
|
179
|
-
context: undefined,
|
|
180
|
-
raw: false
|
|
181
|
-
});
|
|
165
|
+
expect(agent._promiseResponse).calledOnce.calledWith(args);
|
|
182
166
|
});
|
|
183
167
|
});
|
|
184
168
|
|
|
@@ -200,7 +184,7 @@ describe('Agent', () => {
|
|
|
200
184
|
agent._promiseResponse = sinon.stub();
|
|
201
185
|
agent._promiseResponse.resolves('fake-response');
|
|
202
186
|
|
|
203
|
-
return agent.
|
|
187
|
+
return agent.request(options).then((res) => {
|
|
204
188
|
expect(res).to.be.equal('fake-response');
|
|
205
189
|
expect(agent._buildRequest).calledOnce;
|
|
206
190
|
expect(agent._buildRequest).calledWith(options);
|
|
@@ -209,22 +193,65 @@ describe('Agent', () => {
|
|
|
209
193
|
});
|
|
210
194
|
});
|
|
211
195
|
|
|
212
|
-
it('builds a promise to call
|
|
196
|
+
it('builds a promise to call _promiseResponse', () => {
|
|
213
197
|
const agent = new Agent();
|
|
214
198
|
const req = sinon.stub();
|
|
215
|
-
const response =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
199
|
+
const response = {
|
|
200
|
+
ok: true,
|
|
201
|
+
status: 200,
|
|
202
|
+
json: () => Promise.resolve('response')
|
|
203
|
+
};
|
|
204
|
+
req.resolves(response);
|
|
205
|
+
const promise = agent._promiseResponse([], false, req);
|
|
221
206
|
expect(promise).has.property('then');
|
|
222
|
-
return promise.then((
|
|
223
|
-
expect(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
207
|
+
return promise.then((resp) => {
|
|
208
|
+
expect(resp).to.be.eql({
|
|
209
|
+
body: 'response',
|
|
210
|
+
statusCode: 200
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('can handle error responses', () => {
|
|
216
|
+
const failResponseData = [
|
|
217
|
+
{
|
|
218
|
+
name: 'error text includes body error description',
|
|
219
|
+
response: {
|
|
220
|
+
status: 404,
|
|
221
|
+
statusText: 'file not found',
|
|
222
|
+
text: () => Promise.resolve('{"error_description": "file not found"}')
|
|
223
|
+
},
|
|
224
|
+
errorDescription: 'HTTP error 404 from 123.url - file not found'
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'error text with no body description',
|
|
228
|
+
response: {
|
|
229
|
+
status: 404,
|
|
230
|
+
text: () => Promise.resolve(''),
|
|
231
|
+
},
|
|
232
|
+
errorDescription: 'HTTP error 404 from 123.url'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'error text with no status',
|
|
236
|
+
response: {},
|
|
237
|
+
errorDescription: 'Network error from 123.url'
|
|
238
|
+
}
|
|
239
|
+
];
|
|
240
|
+
const agent = new Agent();
|
|
241
|
+
const req = sinon.stub();
|
|
242
|
+
const requests = failResponseData.map((failData) => {
|
|
243
|
+
const response = Object.assign({
|
|
244
|
+
ok: false
|
|
245
|
+
}, failData.response);
|
|
246
|
+
req.resolves(response);
|
|
247
|
+
const promise = agent._promiseResponse(['123.url'] , false, req);
|
|
248
|
+
return promise.catch((resp) => {
|
|
249
|
+
expect(resp.statusCode).to.eql(failData.response.status);
|
|
250
|
+
expect(resp.errorDescription).to.eql(failData.errorDescription);
|
|
251
|
+
expect(resp.shortErrorDescription).to.eql(failData.response.statusText);
|
|
252
|
+
});
|
|
227
253
|
});
|
|
254
|
+
return Promise.all(requests);
|
|
228
255
|
});
|
|
229
256
|
});
|
|
230
257
|
|
|
@@ -232,135 +259,80 @@ describe('Agent', () => {
|
|
|
232
259
|
let agent;
|
|
233
260
|
|
|
234
261
|
beforeEach(() => {
|
|
235
|
-
agent = new Agent();
|
|
262
|
+
agent = new Agent('abc');
|
|
236
263
|
});
|
|
237
264
|
|
|
238
|
-
it('uses
|
|
239
|
-
agent.
|
|
240
|
-
|
|
241
|
-
const req = sinon.stub();
|
|
242
|
-
req.returns({ use: use });
|
|
243
|
-
const result = agent._buildRequest({ uri: 'uri', method: 'get', makerequest: req });
|
|
244
|
-
expect(result).to.be.ok;
|
|
245
|
-
expect(req).to.be.calledWith('get', 'uri');
|
|
246
|
-
expect(use).to.be.calledWith('abc');
|
|
265
|
+
it('uses a baseURL if provided', () => {
|
|
266
|
+
const [uri] = agent._buildRequest({ uri: '/uri', method: 'get' });
|
|
267
|
+
expect(uri).to.equal('abc/uri');
|
|
247
268
|
});
|
|
248
269
|
|
|
249
|
-
it('
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
const result = agent._buildRequest({ uri: 'uri', method: 'get', makerequest: req });
|
|
254
|
-
expect(result).to.be.ok;
|
|
255
|
-
expect(req).to.be.calledWith('get', 'uri');
|
|
256
|
-
expect(use).to.be.notCalled;
|
|
270
|
+
it('uses the provided uri if no baseURL is provided', () => {
|
|
271
|
+
agent.setBaseUrl(undefined);
|
|
272
|
+
const [uri] = agent._buildRequest({ uri: 'uri', method: 'get' });
|
|
273
|
+
expect(uri).to.equal('uri');
|
|
257
274
|
});
|
|
258
275
|
|
|
259
|
-
it('
|
|
260
|
-
|
|
261
|
-
agent.
|
|
262
|
-
|
|
263
|
-
const context = { foo: {} };
|
|
264
|
-
const req = sinon.stub().returns(request);
|
|
265
|
-
agent._buildRequest({ uri: 'uri', method: 'get', context, makerequest: req });
|
|
266
|
-
expect(agent._applyContext).to.be.calledWith(sinon.match.same(request), sinon.match.same(context));
|
|
276
|
+
it('generates context headers when one is provided', () => {
|
|
277
|
+
const context = { tool: { name: 'spanner' } };
|
|
278
|
+
const [, opts] = agent._buildRequest({ uri: '/uri', method: 'get', context });
|
|
279
|
+
expect(opts.headers).to.have.property('X-Particle-Tool', 'spanner');
|
|
267
280
|
});
|
|
268
281
|
|
|
269
|
-
it('
|
|
270
|
-
|
|
271
|
-
agent.
|
|
272
|
-
|
|
273
|
-
const req = sinon.stub().returns(request);
|
|
274
|
-
agent._buildRequest({ uri: 'uri', method: 'get', makerequest: req });
|
|
275
|
-
expect(agent._applyContext).to.not.be.called;
|
|
282
|
+
it('generates auth headers when an auth token is provided', () => {
|
|
283
|
+
const auth = 'abcd-1235';
|
|
284
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', auth });
|
|
285
|
+
expect(opts.headers).to.have.property('Authorization', `Bearer ${auth}`);
|
|
276
286
|
});
|
|
277
287
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
agent.
|
|
281
|
-
|
|
282
|
-
const req = sinon.stub();
|
|
283
|
-
req.returns(request);
|
|
284
|
-
const authorize = sinon.stub();
|
|
285
|
-
agent._authorizationHeader = authorize;
|
|
286
|
-
agent._buildRequest({ uri: 'uri', method: 'get', auth: '123', makerequest: req });
|
|
287
|
-
expect(authorize).to.be.calledWith(sinon.match.same(request), '123');
|
|
288
|
+
it('adds new query params with the given query object', () => {
|
|
289
|
+
const query = { foo: 1, bar: 2 };
|
|
290
|
+
const [uri] = agent._buildRequest({ uri: '/uri', method: 'get', query });
|
|
291
|
+
expect(uri).to.equal('abc/uri?foo=1&bar=2');
|
|
288
292
|
});
|
|
289
293
|
|
|
290
|
-
it('
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
req.returns({ query: query, authorize: sinon.stub() });
|
|
295
|
-
agent._buildRequest({ uri: 'uri', method: 'get', query: '123', makerequest: req });
|
|
296
|
-
expect(query).to.be.calledWith('123');
|
|
294
|
+
it('adds query params without colliding with existing ones', () => {
|
|
295
|
+
const query = { foo: 1, bar: 2 };
|
|
296
|
+
const [uri] = agent._buildRequest({ uri: '/uri?test=true', method: 'get', query });
|
|
297
|
+
expect(uri).to.equal('abc/uri?test=true&foo=1&bar=2');
|
|
297
298
|
});
|
|
298
299
|
|
|
299
|
-
it('
|
|
300
|
-
agent.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
req.returns({ query: query, _authorizationHeader: sinon.stub() });
|
|
304
|
-
agent._buildRequest({ uri: 'uri', method: 'get', makerequest: req });
|
|
305
|
-
expect(query).to.be.notCalled;
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it('should invoke send when data given', () => {
|
|
309
|
-
agent.prefix = undefined;
|
|
310
|
-
const req = sinon.stub();
|
|
311
|
-
const send = sinon.stub();
|
|
312
|
-
req.returns({ send: send });
|
|
313
|
-
agent._buildRequest({ uri: 'uri', method: 'get', data: 'abcd', makerequest: req });
|
|
314
|
-
expect(send).to.be.calledWith('abcd');
|
|
300
|
+
it('adds the provided data as a JSON request body', () => {
|
|
301
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', data: { a: 'abcd' } });
|
|
302
|
+
expect(opts.body).to.eql('{"a":"abcd"}');
|
|
303
|
+
expect(opts.headers).to.have.property('Content-Type', 'application/json');
|
|
315
304
|
});
|
|
316
305
|
|
|
317
306
|
it('should setup form send when form data is given', () => {
|
|
318
|
-
agent.
|
|
319
|
-
|
|
320
|
-
const send = sinon.stub();
|
|
321
|
-
const type = sinon.stub();
|
|
322
|
-
req.returns({ send: send, type: type });
|
|
323
|
-
agent._buildRequest({ uri: 'uri', method: 'get', form: 'abcd', makerequest: req });
|
|
324
|
-
expect(send).to.be.calledWith('abcd');
|
|
325
|
-
expect(type).to.be.calledWith('form');
|
|
307
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', form: { a: 'abcd' } });
|
|
308
|
+
expect(opts.body).to.eql('a=abcd');
|
|
326
309
|
});
|
|
327
310
|
|
|
328
311
|
it('should attach files', () => {
|
|
329
|
-
agent.prefix = undefined;
|
|
330
|
-
const req = sinon.stub();
|
|
331
|
-
const attach = sinon.stub();
|
|
332
|
-
req.returns({ attach: attach });
|
|
333
312
|
const files = {
|
|
334
|
-
file: { data: 'filedata', path: 'filepath' },
|
|
335
|
-
file2: { data: 'file2data', path: 'file2path' }
|
|
313
|
+
file: { data: makeFile('filedata'), path: 'filepath' },
|
|
314
|
+
file2: { data: makeFile('file2data'), path: 'file2path' }
|
|
336
315
|
};
|
|
337
|
-
agent._buildRequest({ uri: 'uri', method: 'get', files
|
|
338
|
-
expect(
|
|
339
|
-
expect(
|
|
340
|
-
expect(
|
|
316
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', files });
|
|
317
|
+
expect(opts.body.toString()).to.equal('[object FormData]');
|
|
318
|
+
expect(extractFilename(opts.body, 'file', 0)).to.eql('filepath');
|
|
319
|
+
expect(extractFilename(opts.body, 'file2', 3)).to.eql('file2path');
|
|
320
|
+
expect(opts.headers).to.not.have.property('Content-Type');
|
|
341
321
|
});
|
|
342
322
|
|
|
343
323
|
it('should attach files and form data', () => {
|
|
344
|
-
agent.prefix = undefined;
|
|
345
|
-
const req = sinon.stub();
|
|
346
|
-
const attach = sinon.stub();
|
|
347
|
-
const field = sinon.stub();
|
|
348
|
-
req.returns({
|
|
349
|
-
attach: attach,
|
|
350
|
-
field: field
|
|
351
|
-
});
|
|
352
324
|
const files = {
|
|
353
|
-
file: { data: 'filedata', path: 'filepath' },
|
|
354
|
-
file2: { data: 'file2data', path: 'file2path' }
|
|
325
|
+
file: { data: makeFile('filedata'), path: 'filepath' },
|
|
326
|
+
file2: { data: makeFile('file2data'), path: 'file2path' }
|
|
355
327
|
};
|
|
356
328
|
const form = { form1: 'value1', form2: 'value2' };
|
|
357
|
-
agent._buildRequest({ uri: 'uri', method: 'get', files
|
|
358
|
-
expect(
|
|
359
|
-
expect(
|
|
360
|
-
expect(
|
|
361
|
-
expect(
|
|
362
|
-
expect(
|
|
363
|
-
expect(
|
|
329
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', files, form });
|
|
330
|
+
expect(opts.body.toString()).to.equal('[object FormData]');
|
|
331
|
+
expect(extractFilename(opts.body, 'file', 0)).to.eql('filepath');
|
|
332
|
+
expect(extractFilename(opts.body, 'file2', 3)).to.eql('file2path');
|
|
333
|
+
expect(extractFormName(opts.body, 'form1', 6, true)).to.eql('value1');
|
|
334
|
+
expect(extractFormName(opts.body, 'form2', 9, true)).to.eql('value2');
|
|
335
|
+
expect(opts.headers).to.not.have.property('Content-Type');
|
|
364
336
|
});
|
|
365
337
|
|
|
366
338
|
it('should handle nested dirs', () => {
|
|
@@ -368,9 +340,14 @@ describe('Agent', () => {
|
|
|
368
340
|
file: { data: makeFile('filedata'), path: 'filepath.ino' },
|
|
369
341
|
file2: { data: makeFile('file2data'), path: 'dir/file2path.cpp' }
|
|
370
342
|
};
|
|
371
|
-
const
|
|
372
|
-
expect(extractFilename(
|
|
373
|
-
expect(extractFilename(
|
|
343
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', files });
|
|
344
|
+
expect(extractFilename(opts.body, 'file', 0)).to.eql('filepath.ino');
|
|
345
|
+
expect(extractFilename(opts.body, 'file2', 3)).to.eql('dir/file2path.cpp');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('sets the user agent to particle-api-js', () => {
|
|
349
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get' });
|
|
350
|
+
expect(opts.headers).to.have.property('User-Agent').that.match(/^particle-api-js/);
|
|
374
351
|
});
|
|
375
352
|
|
|
376
353
|
if (!inBrowser()){
|
|
@@ -378,8 +355,8 @@ describe('Agent', () => {
|
|
|
378
355
|
const files = {
|
|
379
356
|
file: { data: makeFile('filedata'), path: 'dir\\windowsfilepath.cpp' }
|
|
380
357
|
};
|
|
381
|
-
const
|
|
382
|
-
expect(extractFilename(
|
|
358
|
+
const [, opts] = agent._buildRequest({ uri: 'uri', method: 'get', files });
|
|
359
|
+
expect(extractFilename(opts.body, 'file', 0)).to.eql('dir/windowsfilepath.cpp');
|
|
383
360
|
});
|
|
384
361
|
}
|
|
385
362
|
|
|
@@ -402,93 +379,13 @@ describe('Agent', () => {
|
|
|
402
379
|
return /filename="([^"]*)"/.exec(formData._streams[fieldIndex])[1];
|
|
403
380
|
}
|
|
404
381
|
}
|
|
405
|
-
});
|
|
406
382
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const request = {
|
|
414
|
-
end: function end(callback){
|
|
415
|
-
callback(undefined, response);
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
const end = sinon.spy(request, 'end');
|
|
419
|
-
|
|
420
|
-
const agent = new Agent();
|
|
421
|
-
const result = agent._sendRequest(request, fulfill, reject);
|
|
422
|
-
expect(result).to.be.undefined;
|
|
423
|
-
expect(end).to.be.calledOnce;
|
|
424
|
-
|
|
425
|
-
expect(fulfill).to.be.calledOnce;
|
|
426
|
-
// not called with response directly but with an object that is equivalent
|
|
427
|
-
expect(fulfill).to.be.calledWith(response);
|
|
428
|
-
expect(reject).to.not.be.called;
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
const failResponseData = [
|
|
432
|
-
{
|
|
433
|
-
name: 'error text includes body error description',
|
|
434
|
-
response: {
|
|
435
|
-
body: {
|
|
436
|
-
error_description: 'file not found'
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
error: { status: 404 },
|
|
440
|
-
errorDescription: 'HTTP error 404 from 123.url - file not found'
|
|
441
|
-
},
|
|
442
|
-
|
|
443
|
-
{
|
|
444
|
-
name: 'error text with no body description',
|
|
445
|
-
response: { body: {} },
|
|
446
|
-
error: { status: 404 },
|
|
447
|
-
errorDescription: 'HTTP error 404 from 123.url'
|
|
448
|
-
},
|
|
449
|
-
|
|
450
|
-
{
|
|
451
|
-
name: 'error text with no body',
|
|
452
|
-
response: { },
|
|
453
|
-
error: { status: 404 },
|
|
454
|
-
errorDescription: 'HTTP error 404 from 123.url'
|
|
455
|
-
},
|
|
456
|
-
|
|
457
|
-
{
|
|
458
|
-
name: 'error text with no response',
|
|
459
|
-
error: { status: 404 },
|
|
460
|
-
errorDescription: 'HTTP error 404 from 123.url' },
|
|
461
|
-
|
|
462
|
-
{
|
|
463
|
-
name: 'error text with no status',
|
|
464
|
-
error: {},
|
|
465
|
-
errorDescription: 'Network error from 123.url'
|
|
383
|
+
function extractFormName(formData, fieldName, fieldIndex){
|
|
384
|
+
if (inBrowser()){
|
|
385
|
+
return formData.get(fieldName);
|
|
386
|
+
} else {
|
|
387
|
+
return formData._streams[fieldIndex + 1];
|
|
466
388
|
}
|
|
467
|
-
];
|
|
468
|
-
for (let failData of failResponseData){
|
|
469
|
-
it(`can retrieve an error response - ${failData.name}`, () => {
|
|
470
|
-
const fulfill = sinon.stub();
|
|
471
|
-
const reject = sinon.stub();
|
|
472
|
-
|
|
473
|
-
const request = {
|
|
474
|
-
url: '123.url', end: function end(callback){
|
|
475
|
-
callback(failData.error, failData.response);
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
const end = sinon.spy(request, 'end');
|
|
479
|
-
|
|
480
|
-
const agent = new Agent();
|
|
481
|
-
const result = agent._sendRequest(request, fulfill, reject);
|
|
482
|
-
expect(result).to.be.undefined;
|
|
483
|
-
expect(end).to.be.calledOnce;
|
|
484
|
-
expect(fulfill).to.not.be.called;
|
|
485
|
-
expect(reject).to.be.calledWithMatch({
|
|
486
|
-
statusCode: failData.error.status,
|
|
487
|
-
errorDescription: failData.errorDescription,
|
|
488
|
-
error:failData.error,
|
|
489
|
-
body:failData.response ? failData.response.body : undefined
|
|
490
|
-
});
|
|
491
|
-
});
|
|
492
389
|
}
|
|
493
390
|
});
|
|
494
391
|
|
|
@@ -513,57 +410,46 @@ describe('Agent', () => {
|
|
|
513
410
|
});
|
|
514
411
|
});
|
|
515
412
|
|
|
516
|
-
describe('
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
beforeEach(() => {
|
|
520
|
-
req = { set: sinon.stub() };
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
it('applies the tool context when defined', () => {
|
|
413
|
+
describe('_getContextHeaders', () => {
|
|
414
|
+
it('generates the tool context when defined', () => {
|
|
524
415
|
const context = { tool: { name: 'spanner' } };
|
|
525
|
-
agent.
|
|
526
|
-
expect(
|
|
527
|
-
expect(req.set).to.have.been.calledWith('X-Particle-Tool', 'spanner');
|
|
416
|
+
const subject = agent._getContextHeaders(context);
|
|
417
|
+
expect(subject).to.have.property('X-Particle-Tool', 'spanner');
|
|
528
418
|
});
|
|
529
419
|
|
|
530
|
-
it('does not
|
|
420
|
+
it('does not add the tool context header when not defined',() => {
|
|
531
421
|
const context = { tool: { name2: 'spanner' } };
|
|
532
|
-
agent.
|
|
533
|
-
expect(
|
|
422
|
+
const subject = agent._getContextHeaders(context);
|
|
423
|
+
expect(subject).to.not.have.property('X-Particle-Tool');
|
|
534
424
|
});
|
|
535
425
|
|
|
536
|
-
it('
|
|
426
|
+
it('generates the project context header when defined',() => {
|
|
537
427
|
const context = { project: { name: 'blinky' } };
|
|
538
|
-
agent.
|
|
539
|
-
expect(
|
|
540
|
-
expect(req.set).to.have.been.calledWith('X-Particle-Project', 'blinky');
|
|
428
|
+
const subject = agent._getContextHeaders(context);
|
|
429
|
+
expect(subject).to.have.property('X-Particle-Project', 'blinky');
|
|
541
430
|
});
|
|
542
431
|
|
|
543
|
-
it('does not
|
|
432
|
+
it('does not generate the project context header when not defined',() => {
|
|
544
433
|
const context = { project: { name2: 'blinky' } };
|
|
545
|
-
agent.
|
|
546
|
-
expect(
|
|
434
|
+
const subject = agent._getContextHeaders(context);
|
|
435
|
+
expect(subject).to.not.have.property('X-Particle-Project');
|
|
547
436
|
});
|
|
548
437
|
});
|
|
549
438
|
|
|
550
|
-
describe('
|
|
439
|
+
describe('_getToolContext', () => {
|
|
551
440
|
it('does not add a header when the tool name is not defined', () => {
|
|
552
|
-
const req = { set: sinon.stub() };
|
|
553
441
|
const tool = { noname: 'cli' };
|
|
554
|
-
agent.
|
|
555
|
-
expect(
|
|
442
|
+
const subject = agent._getToolContext(tool);
|
|
443
|
+
expect(subject).to.eql({});
|
|
556
444
|
});
|
|
557
445
|
|
|
558
446
|
it('adds a header when the tool is defined', () => {
|
|
559
|
-
const req = { set: sinon.stub() };
|
|
560
447
|
const tool = { name: 'cli' };
|
|
561
|
-
agent.
|
|
562
|
-
expect(
|
|
448
|
+
const subject = agent._getToolContext(tool);
|
|
449
|
+
expect(subject).to.eql({ 'X-Particle-Tool': 'cli' });
|
|
563
450
|
});
|
|
564
451
|
|
|
565
452
|
it('adds a header when the tool and components is defined', () => {
|
|
566
|
-
const req = { set: sinon.stub() };
|
|
567
453
|
const tool = {
|
|
568
454
|
name: 'cli',
|
|
569
455
|
version: '1.2.3',
|
|
@@ -572,24 +458,22 @@ describe('Agent', () => {
|
|
|
572
458
|
{ name: 'foo', version: '0.0.1' }
|
|
573
459
|
]
|
|
574
460
|
};
|
|
575
|
-
agent.
|
|
576
|
-
expect(
|
|
461
|
+
const subject = agent._getToolContext(tool);
|
|
462
|
+
expect(subject).to.eql({ 'X-Particle-Tool': 'cli@1.2.3, bar@a.b.c, foo@0.0.1' });
|
|
577
463
|
});
|
|
578
464
|
});
|
|
579
465
|
|
|
580
466
|
describe('_addProjectContext', () => {
|
|
581
467
|
it('adds a header when the project is defined', () => {
|
|
582
|
-
const req = { set: sinon.stub() };
|
|
583
468
|
const project = { name: 'blinky' };
|
|
584
|
-
agent.
|
|
585
|
-
expect(
|
|
469
|
+
const subject = agent._getProjectContext(project);
|
|
470
|
+
expect(subject).to.have.property('X-Particle-Project', 'blinky');
|
|
586
471
|
});
|
|
587
472
|
|
|
588
473
|
it('does not set the header when the project has no name', () => {
|
|
589
|
-
const req = { set: sinon.stub() };
|
|
590
474
|
const project = { noname: 'blinky' };
|
|
591
|
-
agent.
|
|
592
|
-
expect(
|
|
475
|
+
const subject = agent._getProjectContext(project);
|
|
476
|
+
expect(subject).to.not.have.property('X-Particle-Project');
|
|
593
477
|
});
|
|
594
478
|
});
|
|
595
479
|
|
package/test/EventStream.spec.js
CHANGED
|
@@ -26,7 +26,7 @@ describe('EventStream', () => {
|
|
|
26
26
|
it('creates an EventStream objects', () => {
|
|
27
27
|
const eventStream = new EventStream('uri', 'token');
|
|
28
28
|
|
|
29
|
-
expect(eventStream).to.
|
|
29
|
+
expect(eventStream).to.own.include({ uri: 'uri', token: 'token' });
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
@@ -6,12 +6,12 @@ describe('Particle', () => {
|
|
|
6
6
|
let api;
|
|
7
7
|
|
|
8
8
|
beforeEach(() => {
|
|
9
|
-
api = new Particle();
|
|
9
|
+
api = new Particle({ baseUrl: '' });
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
describe('downloadFile', () => {
|
|
13
13
|
it('download the file', () => {
|
|
14
|
-
const uri = 'https://
|
|
14
|
+
const uri = 'https://binaries.particle.io/libraries/neopixel/neopixel-0.0.10.tar.gz';
|
|
15
15
|
const fileSize = 25505;
|
|
16
16
|
return api.downloadFile({ uri })
|
|
17
17
|
.then(contents => {
|
|
@@ -28,9 +28,10 @@ describe('Particle', () => {
|
|
|
28
28
|
return api.flashTinker('deviceID', 'auth').then(() => {
|
|
29
29
|
expect(api.agent._promiseResponse).to.have.been.calledOnce;
|
|
30
30
|
const req = api.agent._promiseResponse.firstCall.args[0];
|
|
31
|
+
const options = req[1];
|
|
31
32
|
expect(req).to.be.ok;
|
|
32
|
-
expect(
|
|
33
|
-
expect(
|
|
33
|
+
expect(options.headers).to.have.property('X-Particle-Tool').eql('cli@1.2.3');
|
|
34
|
+
expect(options.headers).to.have.property('X-Particle-Project').eql('blinky; version=0.0.1');
|
|
34
35
|
});
|
|
35
36
|
});
|
|
36
37
|
});
|