mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[kfetch] TypeScript-ify (#20914)
In order to make the awesome new kfetch api easier to consume in purely TypeScript projects, and since it's a pretty small module with very few dependencies, I converted it to TypeScript. Along with kfetch I also started a type definition file for `ui/chrome` that we can extend as we go, but will likely be unnecessary after #19992
This commit is contained in:
parent
a091cf0bdb
commit
b434652452
8 changed files with 75 additions and 37 deletions
|
@ -241,6 +241,7 @@
|
|||
"@types/classnames": "^2.2.3",
|
||||
"@types/eslint": "^4.16.2",
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/fetch-mock": "^5.12.2",
|
||||
"@types/getopts": "^2.0.0",
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/hapi-latest": "npm:@types/hapi@17.0.12",
|
||||
|
|
26
src/ui/public/chrome/index.d.ts
vendored
Normal file
26
src/ui/public/chrome/index.d.ts
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare class Chrome {
|
||||
public addBasePath<T = string>(path: T): T;
|
||||
}
|
||||
|
||||
declare const chrome: Chrome;
|
||||
|
||||
export default chrome;
|
|
@ -17,11 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { kfetch } from './kfetch';
|
||||
|
||||
jest.mock('../chrome', () => ({
|
||||
addBasePath: path => `myBase/${path}`,
|
||||
addBasePath: (path: string) => `myBase/${path}`,
|
||||
}));
|
||||
|
||||
jest.mock('../metadata', () => ({
|
||||
|
@ -30,15 +27,14 @@ jest.mock('../metadata', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { kfetch } from './kfetch';
|
||||
|
||||
describe('kfetch', () => {
|
||||
const matcherName = /my\/path/;
|
||||
const matcherName: any = /my\/path/;
|
||||
|
||||
describe('resolves', () => {
|
||||
beforeEach(() =>
|
||||
fetchMock.get({
|
||||
matcher: matcherName,
|
||||
response: new Response(JSON.stringify({ foo: 'bar' })),
|
||||
}));
|
||||
beforeEach(() => fetchMock.get(matcherName, new Response(JSON.stringify({ foo: 'bar' }))));
|
||||
afterEach(() => fetchMock.restore());
|
||||
|
||||
it('should return response', async () => {
|
||||
|
@ -95,11 +91,8 @@ describe('kfetch', () => {
|
|||
|
||||
describe('rejects', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.get({
|
||||
matcher: matcherName,
|
||||
response: {
|
||||
status: 404,
|
||||
},
|
||||
fetchMock.get(matcherName, {
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
afterEach(() => fetchMock.restore());
|
||||
|
@ -107,7 +100,7 @@ describe('kfetch', () => {
|
|||
it('should throw custom error containing response object', () => {
|
||||
return kfetch({
|
||||
pathname: 'my/path',
|
||||
query: { a: 'b' }
|
||||
query: { a: 'b' },
|
||||
}).catch(e => {
|
||||
expect(e.message).toBe('Not Found');
|
||||
expect(e.res.status).toBe(404);
|
|
@ -18,21 +18,30 @@
|
|||
*/
|
||||
|
||||
import 'isomorphic-fetch';
|
||||
import { merge } from 'lodash';
|
||||
import url from 'url';
|
||||
import chrome from '../chrome';
|
||||
|
||||
// @ts-ignore not really worth typing
|
||||
import { metadata } from '../metadata';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
class FetchError extends Error {
|
||||
constructor(res, body) {
|
||||
constructor(public readonly res: Response, public readonly body?: any) {
|
||||
super(res.statusText);
|
||||
this.res = res;
|
||||
this.body = body;
|
||||
Error.captureStackTrace(this, FetchError);
|
||||
}
|
||||
}
|
||||
|
||||
export function kfetch(fetchOptions, kibanaOptions) {
|
||||
export interface KFetchOptions extends RequestInit {
|
||||
pathname?: string;
|
||||
query?: { [key: string]: string | number | boolean };
|
||||
}
|
||||
|
||||
export interface KFetchKibanaOptions {
|
||||
prependBasePath?: boolean;
|
||||
}
|
||||
|
||||
export function kfetch(fetchOptions: KFetchOptions, kibanaOptions?: KFetchKibanaOptions) {
|
||||
// fetch specific options with defaults
|
||||
const { pathname, query, ...combinedFetchOptions } = merge(
|
||||
{
|
||||
|
@ -43,7 +52,7 @@ export function kfetch(fetchOptions, kibanaOptions) {
|
|||
'kbn-version': metadata.version,
|
||||
},
|
||||
},
|
||||
fetchOptions,
|
||||
fetchOptions
|
||||
);
|
||||
|
||||
// kibana specific options with defaults
|
||||
|
@ -57,20 +66,20 @@ export function kfetch(fetchOptions, kibanaOptions) {
|
|||
query,
|
||||
});
|
||||
|
||||
const fetching = new Promise(async (resolve, reject) => {
|
||||
const fetching = new Promise<any>(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));
|
||||
if (res.ok) {
|
||||
return resolve(await res.json());
|
||||
}
|
||||
|
||||
resolve(res.json());
|
||||
try {
|
||||
// attempt to read the body of the response
|
||||
return reject(new FetchError(res, await res.json()));
|
||||
} catch (_) {
|
||||
// send FetchError without the body if we are not be able to read the body for some reason
|
||||
return reject(new FetchError(res));
|
||||
}
|
||||
});
|
||||
|
||||
return fetching;
|
|
@ -17,10 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { kfetchAbortable } from './kfetch_abortable';
|
||||
|
||||
jest.mock('../chrome', () => ({
|
||||
addBasePath: path => `myBase/${path}`,
|
||||
addBasePath: (path: string) => `myBase/${path}`,
|
||||
}));
|
||||
|
||||
jest.mock('../metadata', () => ({
|
||||
|
@ -29,6 +27,8 @@ jest.mock('../metadata', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
import { kfetchAbortable } from './kfetch_abortable';
|
||||
|
||||
describe('kfetchAbortable', () => {
|
||||
it('should return an object with a fetching promise and an abort callback', () => {
|
||||
const { fetching, abort } = kfetchAbortable({ pathname: 'my/path' });
|
|
@ -17,7 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { kfetch } from './kfetch';
|
||||
import { kfetch, KFetchKibanaOptions, KFetchOptions } from './kfetch';
|
||||
|
||||
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
function createAbortable() {
|
||||
const abortController = new AbortController();
|
||||
|
@ -29,7 +31,10 @@ function createAbortable() {
|
|||
};
|
||||
}
|
||||
|
||||
export function kfetchAbortable(fetchOptions, kibanaOptions) {
|
||||
export function kfetchAbortable(
|
||||
fetchOptions?: Omit<KFetchOptions, 'signal'>,
|
||||
kibanaOptions?: KFetchKibanaOptions
|
||||
) {
|
||||
const { signal, abort } = createAbortable();
|
||||
const fetching = kfetch({ ...fetchOptions, signal }, kibanaOptions);
|
||||
|
|
@ -343,6 +343,10 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/fetch-mock@^5.12.2":
|
||||
version "5.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-5.12.2.tgz#8c96517ff74303031c65c5da2d99858e34c844d2"
|
||||
|
||||
"@types/form-data@^2.2.1":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue