Skip to content

Commit

Permalink
Remove abortable promise functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
eliperelman committed May 7, 2019
1 parent 54181b3 commit 8c972fe
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 229 deletions.
49 changes: 0 additions & 49 deletions src/core/public/http/abortable.test.ts

This file was deleted.

38 changes: 0 additions & 38 deletions src/core/public/http/abortable.ts

This file was deleted.

91 changes: 91 additions & 0 deletions src/core/public/http/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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 { merge } from 'lodash';
import { format } from 'url';

import { HttpFetchOptions, HttpBody, Deps } from './types';
import { HttpFetchError } from './http_fetch_error';

const JSON_CONTENT = /^(application\/(json|x-javascript)|text\/(x-)?javascript|x-json)(;.*)?$/;
const NDJSON_CONTENT = /^(application\/ndjson)(;.*)?$/;

export const setup = ({ basePath, injectedMetadata }: Deps) => {
async function fetch(path: string, options: HttpFetchOptions = {}): Promise<HttpBody> {
const { query, prependBasePath, ...fetchOptions } = merge(
{
method: 'GET',
credentials: 'same-origin',
prependBasePath: true,
headers: {
'kbn-version': injectedMetadata.getKibanaVersion(),
'Content-Type': 'application/json',
},
},
options
);
const url = format({
pathname: prependBasePath ? basePath.addToPath(path) : path,
query,
});

if (
options.headers &&
'Content-Type' in options.headers &&
options.headers['Content-Type'] === undefined
) {
delete fetchOptions.headers['Content-Type'];
}

let response;
let body = null;

try {
response = await window.fetch(url, fetchOptions as RequestInit);
} catch (err) {
throw new HttpFetchError(err.message);
}

const contentType = response.headers.get('Content-Type') || '';

try {
if (NDJSON_CONTENT.test(contentType)) {
body = await response.blob();
} else if (JSON_CONTENT.test(contentType)) {
body = await response.json();
} else {
body = await response.text();
}
} catch (err) {
throw new HttpFetchError(err.message, response, body);
}

if (!response.ok) {
throw new HttpFetchError(response.statusText, response, body);
}

return body;
}

function shorthand(method: string) {
return (path: string, options: HttpFetchOptions = {}) => fetch(path, { ...options, method });
}

return { fetch, shorthand };
};
11 changes: 0 additions & 11 deletions src/core/public/http/http_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,17 +248,6 @@ describe('http requests', async () => {

expect(ndjson).toEqual(content);
});

it('should return an abortable promise', () => {
const { http } = setupService();

fetchMock.get('*', {});

const abortable = http.fetch('/my/path');

expect(abortable).toBeInstanceOf(Promise);
expect(typeof abortable.abort).toBe('function');
});
});

describe('addLoadingCount()', async () => {
Expand Down
101 changes: 13 additions & 88 deletions src/core/public/http/http_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,101 +28,26 @@ import {
tap,
} from 'rxjs/operators';

import { merge } from 'lodash';
import { format } from 'url';

import { Deps, HttpFetchOptions, HttpBody } from './types';
import { abortable } from './abortable';
import { HttpFetchError } from './http_fetch_error';

const JSON_CONTENT = /^(application\/(json|x-javascript)|text\/(x-)?javascript|x-json)(;.*)?$/;
const NDJSON_CONTENT = /^(application\/ndjson)(;.*)?$/;
import { Deps } from './types';
import { setup } from './fetch';

/** @internal */
export class HttpService {
private readonly loadingCount$ = new Rx.BehaviorSubject(0);
private readonly stop$ = new Rx.Subject();

public setup({ basePath, injectedMetadata, fatalErrors }: Deps) {
async function fetch(path: string, options: HttpFetchOptions = {}): Promise<HttpBody> {
const { query, prependBasePath, ...fetchOptions } = merge(
{
method: 'GET',
credentials: 'same-origin',
prependBasePath: true,
headers: {
'kbn-version': injectedMetadata.getKibanaVersion(),
'Content-Type': 'application/json',
},
},
options
);
const url = format({
pathname: prependBasePath ? basePath.addToPath(path) : path,
query,
});

if (
options.headers &&
'Content-Type' in options.headers &&
options.headers['Content-Type'] === undefined
) {
delete fetchOptions.headers['Content-Type'];
}

let response;
let body = null;

try {
response = await window.fetch(url, fetchOptions as RequestInit);
} catch (err) {
throw new HttpFetchError(err.message);
}

const contentType = response.headers.get('Content-Type') || '';

try {
if (NDJSON_CONTENT.test(contentType)) {
body = await response.blob();
} else if (JSON_CONTENT.test(contentType)) {
body = await response.json();
} else {
body = await response.text();
}
} catch (err) {
throw new HttpFetchError(err.message, response, body);
}

if (!response.ok) {
throw new HttpFetchError(response.statusText, response, body);
}

return body;
}
public setup(deps: Deps) {
const { fetch, shorthand } = setup(deps);

return {
fetch: abortable<HttpBody>(fetch),
get: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'GET' })
),
head: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'HEAD' })
),
post: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'POST' })
),
put: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'PUT' })
),
patch: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'PATCH' })
),
delete: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'DELETE' })
),
options: abortable<HttpBody>((path: string, options: HttpFetchOptions = {}) =>
fetch(path, { ...options, method: 'OPTIONS' })
),
fetch,
delete: shorthand('HEAD'),
get: shorthand('GET'),
head: shorthand('HEAD'),
options: shorthand('OPTIONS'),
patch: shorthand('PATCH'),
post: shorthand('POST'),
put: shorthand('PUT'),
addLoadingCount: (count$: Rx.Observable<number>) => {
count$
.pipe(
Expand All @@ -149,7 +74,7 @@ export class HttpService {
this.loadingCount$.next(this.loadingCount$.getValue() + delta);
},
error: error => {
fatalErrors.add(error);
deps.fatalErrors.add(error);
},
});
},
Expand Down
5 changes: 0 additions & 5 deletions src/core/public/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,4 @@ export interface HttpFetchOptions extends HttpRequestInit {
prependBasePath?: boolean;
headers?: HttpHeadersInit;
}
export interface Abortable<T> {
abort: () => Promise<T>;
}
export type AbortablePromise<T> = Promise<T> & Abortable<T>;
export type HttpHandler<T> = (path: string, options?: HttpFetchOptions) => Promise<T>;
export type HttpBody = BodyInit | null;
7 changes: 0 additions & 7 deletions src/legacy/ui/public/kfetch/kfetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,6 @@ describe('kfetch', () => {
}
});

it('should return an abortable promise', () => {
const abortable = kfetch({ pathname: '/my/path' });

expect(abortable).toBeInstanceOf(Promise);
expect(typeof abortable.abort).toBe('function');
});

describe('when throwing response error (KFetchError)', async () => {
let error: KFetchError;
beforeEach(async () => {
Expand Down
37 changes: 9 additions & 28 deletions src/legacy/ui/public/kfetch/kfetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,34 +54,15 @@ export function createKfetch(http: HttpSetup) {
options: KFetchOptions,
{ prependBasePath = true }: KFetchKibanaOptions = {}
) {
const controller = options.signal
? { signal: options.signal, abort: Function.prototype }
: new AbortController();
const promise = new Promise((resolve, reject) => {
responseInterceptors(
requestInterceptors(withDefaultOptions(options))
.then(({ pathname, query, ...restOptions }) =>
http.fetch(pathname, {
...restOptions,
signal: controller.signal,
query,
prependBasePath,
})
)
.catch(err => {
throw new KFetchError(err.response || { statusText: err.message }, err.body);
})
).then(resolve, reject);
});

// NOTE: We are only using Object.defineProperty with enumerable:false to be explicit about
// excluding promise.abort() which isn't the case with Object.assign.
return Object.defineProperty(promise, 'abort', {
enumerable: false,
value() {
controller.abort();
},
});
return responseInterceptors(
requestInterceptors(withDefaultOptions(options))
.then(({ pathname, ...restOptions }) =>
http.fetch(pathname, { ...restOptions, prependBasePath })
)
.catch(err => {
throw new KFetchError(err.response || { statusText: err.message }, err.body);
})
);
};
}

Expand Down
Loading

0 comments on commit 8c972fe

Please sign in to comment.