@webex/http-core 3.0.0-beta.3 → 3.0.0-beta.300

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.
Files changed (45) hide show
  1. package/README.md +0 -1
  2. package/dist/http-error-subtypes.js +2 -147
  3. package/dist/http-error-subtypes.js.map +1 -1
  4. package/dist/http-error.js +9 -38
  5. package/dist/http-error.js.map +1 -1
  6. package/dist/index.js +54 -30
  7. package/dist/index.js.map +1 -1
  8. package/dist/interceptors/http-status.js +7 -30
  9. package/dist/interceptors/http-status.js.map +1 -1
  10. package/dist/lib/detect.js +28 -48
  11. package/dist/lib/detect.js.map +1 -1
  12. package/dist/lib/interceptor.js +7 -23
  13. package/dist/lib/interceptor.js.map +1 -1
  14. package/dist/lib/xhr.js +49 -93
  15. package/dist/lib/xhr.js.map +1 -1
  16. package/dist/progress-event.js +0 -7
  17. package/dist/progress-event.js.map +1 -1
  18. package/dist/request/index.js +3 -44
  19. package/dist/request/index.js.map +1 -1
  20. package/dist/request/request.js +5 -23
  21. package/dist/request/request.js.map +1 -1
  22. package/dist/request/request.shim.js +39 -91
  23. package/dist/request/request.shim.js.map +1 -1
  24. package/dist/request/utils.js +91 -0
  25. package/dist/request/utils.js.map +1 -0
  26. package/package.json +10 -10
  27. package/src/http-error-subtypes.js +1 -1
  28. package/src/http-error.js +15 -23
  29. package/src/index.js +59 -9
  30. package/src/interceptors/http-status.js +7 -5
  31. package/src/lib/detect.js +0 -1
  32. package/src/lib/interceptor.js +2 -4
  33. package/src/lib/xhr.js +201 -194
  34. package/src/progress-event.js +10 -5
  35. package/src/request/index.js +4 -36
  36. package/src/request/request.js +16 -14
  37. package/src/request/request.shim.js +51 -39
  38. package/src/request/utils.ts +78 -0
  39. package/test/integration/spec/http-error.js +11 -11
  40. package/test/integration/spec/interceptor.js +20 -13
  41. package/test/integration/spec/progress-event.js +8 -8
  42. package/test/integration/spec/request.js +136 -127
  43. package/test/unit/spec/index.js +58 -0
  44. package/test/unit/spec/interceptors/http-status.js +14 -11
  45. package/test/unit/spec/request/utils.js +77 -0
@@ -14,33 +14,33 @@ describe('http-core', function () {
14
14
  this.timeout(30000);
15
15
  describe('request()', () => {
16
16
  describe('GET', () => {
17
- it('retrieves html', () => request({uri: makeLocalUrl('/')})
18
- .then((res) => {
17
+ it('retrieves html', () =>
18
+ request({uri: makeLocalUrl('/')}).then((res) => {
19
19
  assert.statusCode(res, 200);
20
20
  assert.isString(res.body);
21
21
  }));
22
22
 
23
- it('attaches the request options object to every response', () => request(makeLocalUrl('/'))
24
- .then((res) => {
23
+ it('attaches the request options object to every response', () =>
24
+ request(makeLocalUrl('/')).then((res) => {
25
25
  assert.property(res, 'options');
26
26
  assert.property(res.options, 'uri');
27
27
  assert.equal(res.options.uri, makeLocalUrl('/'));
28
28
  }));
29
29
 
30
- it('accepts `uri` as a separate parameter from `options`', () => request(makeLocalUrl('/'))
31
- .then((res) => {
30
+ it('accepts `uri` as a separate parameter from `options`', () =>
31
+ request(makeLocalUrl('/')).then((res) => {
32
32
  assert.statusCode(res, 200);
33
33
  assert.isString(res.body);
34
34
  }));
35
35
 
36
- it('normalizes `options.url` as `options.uri`', () => request({url: makeLocalUrl('/')})
37
- .then((res) => {
36
+ it('normalizes `options.url` as `options.uri`', () =>
37
+ request({url: makeLocalUrl('/')}).then((res) => {
38
38
  assert.statusCode(res, 200);
39
39
  assert.isString(res.body);
40
40
  }));
41
41
 
42
- it('retrieves JSON', () => request({uri: makeLocalUrl('/json/get')})
43
- .then((res) => {
42
+ it('retrieves JSON', () =>
43
+ request({uri: makeLocalUrl('/json/get')}).then((res) => {
44
44
  assert.statusCode(res, 200);
45
45
  assert.isObject(res.body);
46
46
  assert.deepEqual(res.body, {isObject: true});
@@ -59,83 +59,96 @@ describe('http-core', function () {
59
59
  });
60
60
 
61
61
  describe('with responseType="buffer"', () => {
62
- it('retrieves a file as a buffer', () => request({
63
- uri: makeLocalUrl('/sample-image-small-one.png'),
64
- responseType: 'buffer'
65
- })
66
- .then((res) => {
62
+ it('retrieves a file as a buffer', () =>
63
+ request({
64
+ uri: makeLocalUrl('/sample-image-small-one.png'),
65
+ responseType: 'buffer',
66
+ }).then((res) => {
67
67
  assert.isBufferLike(res.body);
68
68
  }));
69
69
  });
70
70
 
71
71
  describe('with responseType="blob"', () => {
72
- it('retrieves a file as a file', () => request({
73
- uri: makeLocalUrl('/sample-image-small-one.png'),
74
- responseType: 'blob'
75
- })
76
- .then((res) => {
72
+ it('retrieves a file as a file', () =>
73
+ request({
74
+ uri: makeLocalUrl('/sample-image-small-one.png'),
75
+ responseType: 'blob',
76
+ }).then((res) => {
77
77
  assert.isBlobLike(res.body);
78
78
  }));
79
79
  });
80
80
 
81
- it('makes CORS compatible calls', () => request({
82
- uri: 'https://ds.ciscospark.com/v1/region/'
83
- })
84
- .then((res) => {
81
+ // SPARK-413317
82
+ it.skip('makes CORS compatible calls', () =>
83
+ request({
84
+ uri: 'https://ds.ciscospark.com/v1/region/',
85
+ }).then((res) => {
85
86
  assert.notEqual(res.statusCode, 0);
86
87
  }));
87
88
 
88
- it('fails with a subtyped error', () => assert.isRejected(request({
89
- uri: makeLocalUrl('/not-a-route')
90
- }))
91
- .then((err) => {
92
- assert.instanceOf(err, HttpError);
93
- assert.instanceOf(err, HttpError.BadRequest);
94
- }));
89
+ it('fails with a subtyped error', () =>
90
+ assert
91
+ .isRejected(
92
+ request({
93
+ uri: makeLocalUrl('/not-a-route'),
94
+ })
95
+ )
96
+ .then((err) => {
97
+ assert.instanceOf(err, HttpError);
98
+ assert.instanceOf(err, HttpError.BadRequest);
99
+ }));
95
100
 
96
101
  // This is somewhat difficult to test in web browser, but network errors in
97
102
  // browser look like network errors in browsers, so testing it isn`t that
98
103
  // critical. That said, moving the error-reformatting logic out of the
99
104
  // environment-specific implementations may make this easier to stub.
100
- nodeOnly(it)('makes network errors look mostly like HTTP errors', () => assert.isRejected(request('https://localhost:0/not-a-route'))
101
- .then((err) => {
105
+ nodeOnly(it)('makes network errors look mostly like HTTP errors', () =>
106
+ assert.isRejected(request('https://localhost:0/not-a-route')).then((err) => {
102
107
  assert.instanceOf(err, HttpError.NetworkOrCORSError);
103
- }));
108
+ })
109
+ );
104
110
 
105
- it('passes cookies', () => request({
106
- uri: makeLocalUrl('/cookies/set'),
107
- jar: true
108
- })
109
- .then(() => request({
110
- uri: makeLocalUrl('/cookies/expect'),
111
- jar: true
112
- }))
113
- .then((res) => {
114
- assert.statusCode(res, 200);
115
- }));
111
+ it('passes cookies', () =>
112
+ request({
113
+ uri: makeLocalUrl('/cookies/set'),
114
+ jar: true,
115
+ })
116
+ .then(() =>
117
+ request({
118
+ uri: makeLocalUrl('/cookies/expect'),
119
+ jar: true,
120
+ })
121
+ )
122
+ .then((res) => {
123
+ assert.statusCode(res, 200);
124
+ }));
116
125
 
117
126
  // this test fails in Safari 8+
118
127
  nodeOnly(it)('passes cookies to endpoints on other origins', () => {
119
128
  const p1 = request({
120
129
  uri: `http://localhost:${process.env.CORS_PORT}/cookies/set`,
121
- jar: true
130
+ jar: true,
122
131
  })
123
- .then(() => request({
124
- uri: `http://localhost:${process.env.CORS_PORT}/cookies/expect`,
125
- jar: true
126
- }))
132
+ .then(() =>
133
+ request({
134
+ uri: `http://localhost:${process.env.CORS_PORT}/cookies/expect`,
135
+ jar: true,
136
+ })
137
+ )
127
138
  .then((res) => {
128
139
  assert.statusCode(res, 200);
129
140
  });
130
141
 
131
142
  const p2 = request({
132
143
  uri: `http://localhost:${process.env.CORS_PORT}/cookies/set`,
133
- withCredentials: true
144
+ withCredentials: true,
134
145
  })
135
- .then(() => request({
136
- uri: `http://localhost:${process.env.CORS_PORT}/cookies/expect`,
137
- withCredentials: true
138
- }))
146
+ .then(() =>
147
+ request({
148
+ uri: `http://localhost:${process.env.CORS_PORT}/cookies/expect`,
149
+ withCredentials: true,
150
+ })
151
+ )
139
152
  .then((res) => {
140
153
  assert.statusCode(res, 200);
141
154
  });
@@ -148,34 +161,32 @@ describe('http-core', function () {
148
161
  uri: makeLocalUrl('/requires-basic-auth'),
149
162
  auth: {
150
163
  pass: 'basicpass',
151
- user: 'basicuser'
152
- }
153
- })
154
- .then((res) => {
155
- assert.statusCode(res, 200);
156
- });
164
+ user: 'basicuser',
165
+ },
166
+ }).then((res) => {
167
+ assert.statusCode(res, 200);
168
+ });
157
169
 
158
170
  const p2 = request({
159
171
  uri: makeLocalUrl('/requires-basic-auth'),
160
172
  auth: {
161
173
  password: 'basicpass',
162
- username: 'basicuser'
163
- }
164
- })
165
- .then((res) => {
166
- assert.statusCode(res, 200);
167
- });
174
+ username: 'basicuser',
175
+ },
176
+ }).then((res) => {
177
+ assert.statusCode(res, 200);
178
+ });
168
179
 
169
180
  return Promise.all([p1, p2]);
170
181
  });
171
182
 
172
- it('makes Bearer Auth authenticated requests', () => request({
173
- uri: makeLocalUrl('/requires-bearer-auth'),
174
- auth: {
175
- bearer: 'bearertoken'
176
- }
177
- })
178
- .then((res) => {
183
+ it('makes Bearer Auth authenticated requests', () =>
184
+ request({
185
+ uri: makeLocalUrl('/requires-bearer-auth'),
186
+ auth: {
187
+ bearer: 'bearertoken',
188
+ },
189
+ }).then((res) => {
179
190
  assert.statusCode(res, 200);
180
191
  }));
181
192
 
@@ -187,35 +198,34 @@ describe('http-core', function () {
187
198
  object: {
188
199
  string: 'this is a another string',
189
200
  boolean: 'true',
190
- integer: '5'
191
- }
201
+ integer: '5',
202
+ },
192
203
  };
193
204
 
194
205
  return request({
195
206
  uri: makeLocalUrl('/return-qs-as-object'),
196
- qs
197
- })
198
- .then((res) => {
199
- assert.deepEqual(res.body, qs);
200
- });
207
+ qs,
208
+ }).then((res) => {
209
+ assert.deepEqual(res.body, qs);
210
+ });
201
211
  });
202
212
  });
203
213
 
204
214
  flaky(it, process.env.SKIP_FLAKY_TESTS)('submits files as multipart form data', () => {
205
- file.fetch('sample-powerpoint-two-page.ppt')
206
- .then((f) => request({
215
+ file.fetch('sample-powerpoint-two-page.ppt').then((f) =>
216
+ request({
207
217
  method: 'POST',
208
218
  uri: makeLocalUrl('/files/metadata'),
209
219
  formData: {
210
- files: [f]
220
+ files: [f],
211
221
  },
212
- json: true
222
+ json: true,
223
+ }).then((res) => {
224
+ // This asserts that the server received the file and was able to
225
+ // decode its filename.
226
+ assert.equal(res.body[0].originalname, f.name);
213
227
  })
214
- .then((res) => {
215
- // This asserts that the server received the file and was able to
216
- // decode its filename.
217
- assert.equal(res.body[0].originalname, f.name);
218
- }));
228
+ );
219
229
  });
220
230
 
221
231
  ['PUT', 'PATCH', 'POST'].forEach((method) => {
@@ -226,19 +236,18 @@ describe('http-core', function () {
226
236
  object: {
227
237
  string: 'this is a another string',
228
238
  boolean: 'true',
229
- integer: '5'
230
- }
239
+ integer: '5',
240
+ },
231
241
  };
232
242
 
233
243
  return request({
234
244
  method,
235
245
  uri: makeLocalUrl('/json/set'),
236
- body: payload
237
- })
238
- .then((res) => {
239
- assert.statusCode(res, 200);
240
- assert.deepEqual(res.body, payload);
241
- });
246
+ body: payload,
247
+ }).then((res) => {
248
+ assert.statusCode(res, 200);
249
+ assert.deepEqual(res.body, payload);
250
+ });
242
251
  });
243
252
 
244
253
  it('submits urlencoded form data', () =>
@@ -249,41 +258,40 @@ describe('http-core', function () {
249
258
  uri: makeLocalUrl('/form/reflect'),
250
259
  form: {
251
260
  a: 1,
252
- b: 2
253
- }
254
- })
255
- .then((res) => {
256
- assert.statusCode(res, 200);
257
- assert.deepEqual(res.body, {
258
- a: '1',
259
- b: '2'
260
- });
261
- }));
262
-
263
- it('submits files', () => file.fetch('/sample-image-small-one.png')
264
- .then((f) => request({
265
- method,
266
- uri: makeLocalUrl('/files/reflect'),
267
- body: f,
268
- json: false,
269
- // Note: setting response type so the `reflect()ed` response is in
270
- // a form we can use, this is not needed for normal file uploads.
271
- responseType: 'blob'
272
- })
273
- .then((res) => {
261
+ b: 2,
262
+ },
263
+ }).then((res) => {
264
+ assert.statusCode(res, 200);
265
+ assert.deepEqual(res.body, {
266
+ a: '1',
267
+ b: '2',
268
+ });
269
+ }));
270
+
271
+ it('submits files', () =>
272
+ file.fetch('/sample-image-small-one.png').then((f) =>
273
+ request({
274
+ method,
275
+ uri: makeLocalUrl('/files/reflect'),
276
+ body: f,
277
+ json: false,
278
+ // Note: setting response type so the `reflect()ed` response is in
279
+ // a form we can use, this is not needed for normal file uploads.
280
+ responseType: 'blob',
281
+ }).then((res) => {
274
282
  assert.equal(res.body.type, f.type);
275
283
 
276
- return file.isMatchingFile(res.body, f)
277
- .then((result) => assert.isTrue(result));
278
- })));
284
+ return file.isMatchingFile(res.body, f).then((result) => assert.isTrue(result));
285
+ })
286
+ ));
279
287
 
280
- (inBrowser ? it : it.skip)('emits upload progress events', () => file.fetch('/sample-image-small-one.png')
281
- .then((f) => {
288
+ (inBrowser ? it : it.skip)('emits upload progress events', () =>
289
+ file.fetch('/sample-image-small-one.png').then((f) => {
282
290
  const options = {
283
291
  method,
284
292
  uri: makeLocalUrl('/files/reflect'),
285
293
  body: f,
286
- json: false
294
+ json: false,
287
295
  };
288
296
 
289
297
  const promise = request(options);
@@ -295,7 +303,8 @@ describe('http-core', function () {
295
303
  return promise.then(() => {
296
304
  assert.called(spy);
297
305
  });
298
- }));
306
+ })
307
+ );
299
308
  });
300
309
  });
301
310
  });
@@ -0,0 +1,58 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import sinon from 'sinon';
3
+
4
+ import * as utils from '@webex/http-core/src/request/utils';
5
+ import WebexTrackingIdInterceptor from '@webex/webex-core/src/interceptors/webex-tracking-id';
6
+ import UserAgentInterceptor from '@webex/webex-core/src/interceptors/webex-user-agent';
7
+ import {protoprepareFetchOptions, setTimingsAndFetch} from '@webex/http-core/src/index';
8
+
9
+ describe('http-core index tests', () => {
10
+ describe('#protoprepareFetchOptions()', () => {
11
+ it('uses default options and adds expected options', async () => {
12
+ const defaultOptions = {
13
+ interceptors: [WebexTrackingIdInterceptor.create(), UserAgentInterceptor.create()],
14
+ };
15
+ const options = {};
16
+
17
+ const prepareFetchOptions = protoprepareFetchOptions(defaultOptions);
18
+
19
+ await prepareFetchOptions(options);
20
+
21
+ assert.deepEqual(options, {
22
+ headers: {
23
+ trackingid: 'undefined_1',
24
+ 'spark-user-agent': 'webex-js-sdk/development (node)',
25
+ },
26
+ keepalive: true,
27
+ });
28
+
29
+ assert.equal(typeof options.logger, 'object');
30
+ assert.equal(typeof options.upload, 'object');
31
+ assert.equal(typeof options.download, 'object');
32
+ assert.isArray(options.interceptors);
33
+ });
34
+ });
35
+
36
+ describe('#setTimingsAndFetch()', () => {
37
+ const now = Date.now();
38
+ let stubbedFetch;
39
+
40
+ beforeEach(() => {
41
+ global.fetch = sinon.stub();
42
+ });
43
+
44
+ it('calls fetch with expected options', async () => {
45
+ sinon.useFakeTimers(now);
46
+ const options = {uri: 'foo'};
47
+
48
+ const newOptions = setTimingsAndFetch(options);
49
+
50
+ sinon.assert.calledOnce(global.fetch);
51
+ sinon.assert.calledWith(global.fetch, 'foo', {
52
+ uri: 'foo',
53
+ $timings: {requestStart: now, networkStart: now},
54
+ });
55
+ sinon.restore();
56
+ });
57
+ });
58
+ });
@@ -11,9 +11,13 @@ describe('http-core', () => {
11
11
  let interceptor;
12
12
 
13
13
  beforeEach(() => {
14
- interceptor = Reflect.apply(HttpStatusInterceptor.create, {
15
- sessionId: 'mock-webex_uuid'
16
- }, []);
14
+ interceptor = Reflect.apply(
15
+ HttpStatusInterceptor.create,
16
+ {
17
+ sessionId: 'mock-webex_uuid',
18
+ },
19
+ []
20
+ );
17
21
  });
18
22
 
19
23
  describe('#onResponse', () => {
@@ -21,21 +25,20 @@ describe('http-core', () => {
21
25
  const response = {
22
26
  statusCode: 404,
23
27
  body: {
24
- errorCode: 2000002
25
- }
28
+ errorCode: 2000002,
29
+ },
26
30
  };
27
31
 
28
- return interceptor.onResponse({}, response)
29
- .then((result) => {
30
- assert.equal(result, response);
31
- });
32
+ return interceptor.onResponse({}, response).then((result) => {
33
+ assert.equal(result, response);
34
+ });
32
35
  });
33
36
  it('rejects when locus redirect is not intended', () => {
34
37
  const response = {
35
38
  statusCode: 404,
36
39
  body: {
37
- errorCode: 2000001
38
- }
40
+ errorCode: 2000001,
41
+ },
39
42
  };
40
43
 
41
44
  assert.isRejected(interceptor.onResponse({}, response));
@@ -0,0 +1,77 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import sinon from 'sinon';
3
+
4
+ import * as utils from '@webex/http-core/src/request/utils';
5
+ import WebexTrackingIdInterceptor from '@webex/webex-core/src/interceptors/webex-tracking-id';
6
+ import UserAgentInterceptor from '@webex/webex-core/src/interceptors/webex-user-agent';
7
+
8
+ describe('Request utils', () => {
9
+ describe('#intercept()', () => {
10
+ it('updates options from interceptors', async () => {
11
+ const options = {};
12
+ const interceptors = [WebexTrackingIdInterceptor.create(), UserAgentInterceptor.create()];
13
+
14
+ return utils.intercept(options, interceptors, 'Request').then(() => {
15
+ assert.equal(Object.keys(options.headers).length, 2);
16
+ assert.equal(options.headers.trackingid, 'undefined_1');
17
+ assert.equal(options.headers['spark-user-agent'], 'webex-js-sdk/development (node)');
18
+ });
19
+ });
20
+ });
21
+
22
+ describe('#prepareFetchOptions()', () => {
23
+ it('updates options as expected', async () => {
24
+ const options = {
25
+ json: true,
26
+ body: {foo: 'bar'},
27
+ headers: {},
28
+ interceptors: [WebexTrackingIdInterceptor.create(), UserAgentInterceptor.create()],
29
+ };
30
+
31
+ return utils.prepareFetchOptions(options).then(() => {
32
+ assert.deepEqual(options.headers, {
33
+ accept: 'application/json',
34
+ 'content-type': 'application/json',
35
+ trackingid: 'undefined_1',
36
+ 'spark-user-agent': 'webex-js-sdk/development (node)',
37
+ });
38
+
39
+ assert.equal(options.body, '{"foo":"bar"}');
40
+
41
+ assert.equal(options.download != undefined, true);
42
+ assert.equal(options.upload != undefined, true);
43
+ assert.equal(options.keepalive, true);
44
+ });
45
+ });
46
+
47
+ it('updates options as expected when accept and content-type exist', async () => {
48
+ const options = {
49
+ json: true,
50
+ body: {foo: 'bar'},
51
+ headers: {accept: 'foo', 'content-type': 'bar'},
52
+ interceptors: [WebexTrackingIdInterceptor.create(), UserAgentInterceptor.create()],
53
+ };
54
+
55
+ return utils.prepareFetchOptions(options).then(() => {
56
+ assert.deepEqual(options.headers, {
57
+ accept: 'foo',
58
+ 'content-type': 'bar',
59
+ trackingid: 'undefined_1',
60
+ 'spark-user-agent': 'webex-js-sdk/development (node)',
61
+ });
62
+ });
63
+ });
64
+
65
+ it('updates body as expected when json = some object', async () => {
66
+ const options = {
67
+ json: {bar: 'baz'},
68
+ headers: {accept: 'foo', 'content-type': 'bar'},
69
+ interceptors: [WebexTrackingIdInterceptor.create(), UserAgentInterceptor.create()],
70
+ };
71
+
72
+ return utils.prepareFetchOptions(options).then(() => {
73
+ assert.equal(options.body, '{"bar":"baz"}');
74
+ });
75
+ });
76
+ });
77
+ });