Skip to content

Commit

Permalink
[ML] Use Kibana's HttpHandler for HTTP requests (elastic#59320) (elas…
Browse files Browse the repository at this point in the history
…tic#59537)

* [ML] use kibana http

* [ML] fromHttpHandler

* [ML] remove __LEGACY, update asSystemRequest header

* [ML] transform with NP http
  • Loading branch information
darnautov authored Mar 6, 2020
1 parent 5b76472 commit 9ed7800
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 96 deletions.
4 changes: 0 additions & 4 deletions x-pack/legacy/plugins/ml/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ export interface MlDependencies extends AppMountParameters {
data: DataPublicPluginStart;
security: SecurityPluginSetup;
licensing: LicensingPluginSetup;
__LEGACY: {
XSRF: string;
};
}

interface AppProps {
Expand All @@ -49,7 +46,6 @@ const App: FC<AppProps> = ({ coreStart, deps }) => {
recentlyAccessed: coreStart.chrome!.recentlyAccessed,
basePath: coreStart.http.basePath,
savedObjectsClient: coreStart.savedObjects.client,
XSRF: deps.__LEGACY.XSRF,
application: coreStart.application,
http: coreStart.http,
security: deps.security,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ function randomNumber(min, max) {
}

function saveWatch(watchModel) {
const basePath = getBasePath();
const path = basePath.prepend('/api/watcher');
const path = '/api/watcher';
const url = `${path}/watch/${watchModel.id}`;

return http({
Expand Down Expand Up @@ -188,8 +187,7 @@ class CreateWatchService {

loadWatch(jobId) {
const id = `ml-${jobId}`;
const basePath = getBasePath();
const path = basePath.prepend('/api/watcher');
const path = '/api/watcher';
const url = `${path}/watch/${id}`;
return http({
url,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ function initManagementSection() {
setDependencyCache({
docLinks: legacyDocLinks as any,
basePath: legacyBasePath as any,
XSRF: chrome.getXsrfToken(),
});

management.register('ml', {
Expand Down
145 changes: 93 additions & 52 deletions x-pack/legacy/plugins/ml/public/application/services/http_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,66 @@
* you may not use this file except in compliance with the Elastic License.
*/

// service for interacting with the server
import { Observable } from 'rxjs';

import { fromFetch } from 'rxjs/fetch';
import { from, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';

import { getXSRF } from '../util/dependency_cache';

export interface HttpOptions {
url?: string;
}
import { getHttp } from '../util/dependency_cache';

function getResultHeaders(headers: HeadersInit): HeadersInit {
return {
asSystemRequest: false,
asSystemRequest: true,
'Content-Type': 'application/json',
'kbn-version': getXSRF(),
...headers,
} as HeadersInit;
}

export function http(options: any) {
return new Promise((resolve, reject) => {
if (options && options.url) {
let url = '';
url = url + (options.url || '');
const headers = getResultHeaders(options.headers ?? {});

const allHeaders =
options.headers === undefined ? headers : { ...options.headers, ...headers };
const body = options.data === undefined ? null : JSON.stringify(options.data);

const payload: RequestInit = {
method: options.method || 'GET',
headers: allHeaders,
credentials: 'same-origin',
};

if (body !== null) {
payload.body = body;
}
interface HttpOptions {
url: string;
method: string;
headers?: any;
data?: any;
}

fetch(url, payload)
.then(resp => {
resp
.json()
.then(resp.ok === true ? resolve : reject)
.catch(resp.ok === true ? resolve : reject);
})
.catch(resp => {
reject(resp);
});
} else {
reject();
/**
* Function for making HTTP requests to Kibana's backend.
* Wrapper for Kibana's HttpHandler.
*/
export async function http(options: HttpOptions) {
if (!options?.url) {
throw new Error('URL is missing');
}

try {
let url = '';
url = url + (options.url || '');
const headers = getResultHeaders(options.headers ?? {});

const allHeaders = options.headers === undefined ? headers : { ...options.headers, ...headers };
const body = options.data === undefined ? null : JSON.stringify(options.data);

const payload: RequestInit = {
method: options.method || 'GET',
headers: allHeaders,
credentials: 'same-origin',
};

if (body !== null) {
payload.body = body;
}
});

return await getHttp().fetch(url, payload);
} catch (e) {
throw new Error(e);
}
}

interface RequestOptions extends RequestInit {
body: BodyInit | any;
}

/**
* Function for making HTTP requests to Kibana's backend which returns an Observable
* with request cancellation support.
*/
export function http$<T>(url: string, options: RequestOptions): Observable<T> {
const requestInit: RequestInit = {
...options,
Expand All @@ -75,13 +73,56 @@ export function http$<T>(url: string, options: RequestOptions): Observable<T> {
headers: getResultHeaders(options.headers ?? {}),
};

return fromFetch(url, requestInit).pipe(
switchMap(response => {
if (response.ok) {
return from(response.json() as Promise<T>);
return fromHttpHandler<T>(url, requestInit);
}

/**
* Creates an Observable from Kibana's HttpHandler.
*/
export function fromHttpHandler<T>(input: string, init?: RequestInit): Observable<T> {
return new Observable<T>(subscriber => {
const controller = new AbortController();
const signal = controller.signal;

let abortable = true;
let unsubscribed = false;

if (init?.signal) {
if (init.signal.aborted) {
controller.abort();
} else {
throw new Error(String(response.status));
init.signal.addEventListener('abort', () => {
if (!signal.aborted) {
controller.abort();
}
});
}
})
);
}

const perSubscriberInit: RequestInit = {
...(init ? init : {}),
signal,
};

getHttp()
.fetch<T>(input, perSubscriberInit)
.then(response => {
abortable = false;
subscriber.next(response);
subscriber.complete();
})
.catch(err => {
abortable = false;
if (!unsubscribed) {
subscriber.error(err);
}
});

return () => {
unsubscribed = true;
if (abortable) {
controller.abort();
}
};
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ import { filters } from './filters';
import { results } from './results';
import { jobs } from './jobs';
import { fileDatavisualizer } from './datavisualizer';
import { getBasePath } from '../../util/dependency_cache';

export function basePath() {
return getBasePath().prepend('/api/ml');
return '/api/ml';
}

export const ml = {
Expand Down Expand Up @@ -452,7 +451,7 @@ export const ml = {
},

getIndices() {
const tempBasePath = getBasePath().prepend('/api');
const tempBasePath = '/api';
return http({
url: `${tempBasePath}/index_management/indices`,
method: 'GET',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export interface DependencyCache {
autocomplete: DataPublicPluginStart['autocomplete'] | null;
basePath: IBasePath | null;
savedObjectsClient: SavedObjectsClientContract | null;
XSRF: string | null;
application: ApplicationStart | null;
http: HttpStart | null;
security: SecurityPluginSetup | null;
Expand All @@ -54,7 +53,6 @@ const cache: DependencyCache = {
autocomplete: null,
basePath: null,
savedObjectsClient: null,
XSRF: null,
application: null,
http: null,
security: null,
Expand All @@ -73,7 +71,6 @@ export function setDependencyCache(deps: Partial<DependencyCache>) {
cache.autocomplete = deps.autocomplete || null;
cache.basePath = deps.basePath || null;
cache.savedObjectsClient = deps.savedObjectsClient || null;
cache.XSRF = deps.XSRF || null;
cache.application = deps.application || null;
cache.http = deps.http || null;
cache.security = deps.security || null;
Expand Down Expand Up @@ -162,13 +159,6 @@ export function getSavedObjectsClient() {
return cache.savedObjectsClient;
}

export function getXSRF() {
if (cache.XSRF === null) {
throw new Error("xsrf hasn't been initialized");
}
return cache.XSRF;
}

export function getApplication() {
if (cache.application === null) {
throw new Error("application hasn't been initialized");
Expand Down
4 changes: 0 additions & 4 deletions x-pack/legacy/plugins/ml/public/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

import chrome from 'ui/chrome';
import { npSetup, npStart } from 'ui/new_platform';
import { PluginInitializerContext } from 'src/core/public';
import { SecurityPluginSetup } from '../../../../plugins/security/public';
Expand All @@ -26,8 +25,5 @@ export const setup = pluginInstance.setup(npSetup.core, {
data: npStart.plugins.data,
security: setupDependencies.security,
licensing: setupDependencies.licensing,
__LEGACY: {
XSRF: chrome.getXsrfToken(),
},
});
export const start = pluginInstance.start(npStart.core, npStart.plugins);
3 changes: 1 addition & 2 deletions x-pack/legacy/plugins/ml/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
import { MlDependencies } from './application/app';

export class MlPlugin implements Plugin<Setup, Start> {
setup(core: CoreSetup, { data, security, licensing, __LEGACY }: MlDependencies) {
setup(core: CoreSetup, { data, security, licensing }: MlDependencies) {
core.application.register({
id: 'ml',
title: 'Machine learning',
Expand All @@ -21,7 +21,6 @@ export class MlPlugin implements Plugin<Setup, Start> {
onAppLeave: params.onAppLeave,
history: params.history,
data,
__LEGACY,
security,
licensing,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const setAppDependencies = (deps: AppDependencies) => {
autocomplete: deps.plugins.data.autocomplete,
docLinks: deps.core.docLinks,
basePath: legacyBasePath as any,
XSRF: deps.plugins.xsrfToken,
});
DependenciesContext = createContext<AppDependencies>(deps);
return DependenciesContext.Provider;
Expand Down
7 changes: 3 additions & 4 deletions x-pack/legacy/plugins/transform/public/app/hooks/use_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,9 @@ const apiFactory = (basePath: string, indicesBasePath: string, http: Http) => ({
export const useApi = () => {
const appDeps = useAppDependencies();

const basePath = appDeps.core.http.basePath.prepend('/api/transform');
const indicesBasePath = appDeps.core.http.basePath.prepend('/api');
const xsrfToken = appDeps.plugins.xsrfToken;
const http = httpFactory(xsrfToken);
const basePath = '/api/transform';
const indicesBasePath = '/api';
const http = httpFactory(appDeps.core.http);

return apiFactory(basePath, indicesBasePath, http);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { HttpSetup } from 'kibana/public';
// service for interacting with the server
import { Dictionary } from '../../../common/types/common';

export type Http = (options: Dictionary<any>) => Promise<unknown>;

export function httpFactory(xsrfToken: string) {
export function httpFactory(httpSetup: HttpSetup) {
return function http(options: Dictionary<any>) {
return new Promise((resolve, reject) => {
if (options && options.url) {
let url = '';
url = url + (options.url || '');
const headers = {
'kbn-system-request': true,
'Content-Type': 'application/json',
'kbn-version': xsrfToken,
...options.headers,
};

Expand All @@ -36,9 +35,10 @@ export function httpFactory(xsrfToken: string) {
payload.body = body;
}

fetch(url, payload)
httpSetup
.fetch(url, payload)
.then(resp => {
resp.json().then(resp.ok === true ? resolve : reject);
resolve(resp);
})
.catch(resp => {
reject(resp);
Expand Down
3 changes: 1 addition & 2 deletions x-pack/legacy/plugins/transform/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Plugin {
savedObjects,
overlays,
} = core;
const { data, management, uiMetric, xsrfToken } = plugins;
const { data, management, uiMetric } = plugins;

// AppCore/AppPlugins to be passed on as React context
const appDependencies = {
Expand All @@ -45,7 +45,6 @@ export class Plugin {
plugins: {
data,
management,
xsrfToken,
},
};

Expand Down
Loading

0 comments on commit 9ed7800

Please sign in to comment.