Add ability to abort a kfetch call. (#20700) (#20748)

* Split kfetch module into kfetch and kfetchAbortable sub-modules.
* Add abortcontroller-polyfill.
This commit is contained in:
CJ Cenizal 2018-07-12 16:20:59 -07:00 committed by GitHub
parent e5a6f2b453
commit 3d2a1613ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 55 deletions

View file

@ -86,6 +86,7 @@
"@kbn/ui-framework": "link:packages/kbn-ui-framework",
"JSONStream": "1.1.1",
"accept-language-parser": "1.2.0",
"abortcontroller-polyfill": "^1.1.9",
"angular": "1.6.9",
"angular-aria": "1.6.6",
"angular-elastic": "2.5.0",

View file

@ -21,9 +21,13 @@ import _ from 'lodash';
import angular from 'angular';
import { metadata } from '../metadata';
// Polyfills
import 'babel-polyfill';
import 'whatwg-fetch';
import 'custom-event-polyfill';
import 'abortcontroller-polyfill';
import '../state_management/global_state';
import '../config';
import '../notify';

View file

@ -17,57 +17,5 @@
* under the License.
*/
import 'isomorphic-fetch';
import url from 'url';
import chrome from '../chrome';
import { metadata } from '../metadata';
import { merge } from 'lodash';
class FetchError extends Error {
constructor(res, body) {
super(res.statusText);
this.res = res;
this.body = body;
Error.captureStackTrace(this, FetchError);
}
}
export async function kfetch(fetchOptions, kibanaOptions) {
// fetch specific options with defaults
const { pathname, query, ...combinedFetchOptions } = merge(
{
method: 'GET',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'kbn-version': metadata.version,
},
},
fetchOptions
);
// kibana specific options with defaults
const combinedKibanaOptions = {
prependBasePath: true,
...kibanaOptions,
};
const fullUrl = url.format({
pathname: combinedKibanaOptions.prependBasePath ? chrome.addBasePath(pathname) : pathname,
query,
});
const res = await fetch(fullUrl, combinedFetchOptions);
if (!res.ok) {
let body;
try {
body = await res.json();
} catch (err) {
// ignore error, may not be able to get body for response that is not ok
}
throw new FetchError(res, body);
}
return res.json();
}
export { kfetch } from './kfetch';
export { kfetchAbortable } from './kfetch_abortable';

View file

@ -0,0 +1,77 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import 'isomorphic-fetch';
import url from 'url';
import chrome from '../chrome';
import { metadata } from '../metadata';
import { merge } from 'lodash';
class FetchError extends Error {
constructor(res, body) {
super(res.statusText);
this.res = res;
this.body = body;
Error.captureStackTrace(this, FetchError);
}
}
export function kfetch(fetchOptions, kibanaOptions) {
// fetch specific options with defaults
const { pathname, query, ...combinedFetchOptions } = merge(
{
method: 'GET',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'kbn-version': metadata.version,
},
},
fetchOptions,
);
// kibana specific options with defaults
const combinedKibanaOptions = {
prependBasePath: true,
...kibanaOptions,
};
const fullUrl = url.format({
pathname: combinedKibanaOptions.prependBasePath ? chrome.addBasePath(pathname) : pathname,
query,
});
const fetching = new Promise(async (resolve, reject) => {
const res = await fetch(fullUrl, combinedFetchOptions);
if (!res.ok) {
let body;
try {
body = await res.json();
} catch (err) {
// ignore error, may not be able to get body for response that is not ok
}
return reject(new FetchError(res, body));
}
resolve(res.json());
});
return fetching;
}

View file

@ -18,11 +18,12 @@
*/
import fetchMock from 'fetch-mock';
import { kfetch } from './index';
import { kfetch } from './kfetch';
jest.mock('../chrome', () => ({
addBasePath: path => `myBase/${path}`,
}));
jest.mock('../metadata', () => ({
metadata: {
version: 'my-version',

View file

@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { kfetch } from './kfetch';
function createAbortable() {
const abortController = new AbortController();
const { signal, abort } = abortController;
return {
signal,
abort: abort.bind(abortController),
};
}
export function kfetchAbortable(fetchOptions, kibanaOptions) {
const { signal, abort } = createAbortable();
const fetching = kfetch({ ...fetchOptions, signal }, kibanaOptions);
return {
fetching,
abort,
};
}

View file

@ -0,0 +1,39 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { kfetchAbortable } from './kfetch_abortable';
jest.mock('../chrome', () => ({
addBasePath: path => `myBase/${path}`,
}));
jest.mock('../metadata', () => ({
metadata: {
version: 'my-version',
},
}));
describe('kfetchAbortable', () => {
it('should return an object with a fetching promise and an abort callback', () => {
const { fetching, abort } = kfetchAbortable({ pathname: 'my/path' });
expect(typeof fetching.then).toBe('function');
expect(typeof fetching.catch).toBe('function');
expect(typeof abort).toBe('function');
});
});

View file

@ -545,6 +545,10 @@ abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
abortcontroller-polyfill@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.1.9.tgz#9fefe359fda2e9e0932dc85e6106453ac393b2da"
accept-language-parser@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/accept-language-parser/-/accept-language-parser-1.2.0.tgz#6a18942acab3f090a4a09590e03101a99fa22bff"