[ML] Use Kibana's HttpHandler for HTTP requests (#59320)
* [ML] use kibana http * [ML] fromHttpHandler * [ML] remove __LEGACY, update asSystemRequest header * [ML] transform with NP http
This commit is contained in:
parent
2817d6e3a8
commit
3a53fe8e45
|
@ -25,9 +25,6 @@ export interface MlDependencies extends AppMountParameters {
|
|||
data: DataPublicPluginStart;
|
||||
security: SecurityPluginSetup;
|
||||
licensing: LicensingPluginSetup;
|
||||
__LEGACY: {
|
||||
XSRF: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AppProps {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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({
|
||||
|
@ -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,
|
||||
|
|
|
@ -54,7 +54,6 @@ function initManagementSection() {
|
|||
setDependencyCache({
|
||||
docLinks: legacyDocLinks as any,
|
||||
basePath: legacyBasePath as any,
|
||||
XSRF: chrome.getXsrfToken(),
|
||||
});
|
||||
|
||||
management.register('ml', {
|
||||
|
|
|
@ -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 ?? {});
|
||||
interface HttpOptions {
|
||||
url: string;
|
||||
method: string;
|
||||
headers?: any;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
const allHeaders =
|
||||
options.headers === undefined ? headers : { ...options.headers, ...headers };
|
||||
const body = options.data === undefined ? null : JSON.stringify(options.data);
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
|
||||
const payload: RequestInit = {
|
||||
method: options.method || 'GET',
|
||||
headers: allHeaders,
|
||||
credentials: 'same-origin',
|
||||
};
|
||||
try {
|
||||
let url = '';
|
||||
url = url + (options.url || '');
|
||||
const headers = getResultHeaders(options.headers ?? {});
|
||||
|
||||
if (body !== null) {
|
||||
payload.body = body;
|
||||
}
|
||||
const allHeaders = options.headers === undefined ? headers : { ...options.headers, ...headers };
|
||||
const body = options.data === undefined ? null : JSON.stringify(options.data);
|
||||
|
||||
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();
|
||||
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,
|
||||
|
@ -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>);
|
||||
} else {
|
||||
throw new Error(String(response.status));
|
||||
}
|
||||
})
|
||||
);
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
|
@ -54,7 +53,6 @@ const cache: DependencyCache = {
|
|||
autocomplete: null,
|
||||
basePath: null,
|
||||
savedObjectsClient: null,
|
||||
XSRF: null,
|
||||
application: null,
|
||||
http: null,
|
||||
security: null,
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
|
|
@ -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';
|
||||
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
@ -21,7 +21,6 @@ export class MlPlugin implements Plugin<Setup, Start> {
|
|||
onAppLeave: params.onAppLeave,
|
||||
history: params.history,
|
||||
data,
|
||||
__LEGACY,
|
||||
security,
|
||||
licensing,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
@ -45,7 +45,6 @@ export class Plugin {
|
|||
plugins: {
|
||||
data,
|
||||
management,
|
||||
xsrfToken,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { docTitle } from 'ui/doc_title/doc_title';
|
||||
|
||||
// @ts-ignore: allow traversal to fail on x-pack build
|
||||
|
@ -32,7 +31,7 @@ export type AppCore = Pick<
|
|||
| 'overlays'
|
||||
| 'notifications'
|
||||
>;
|
||||
export type AppPlugins = Pick<ShimPlugins, 'data' | 'management' | 'xsrfToken'>;
|
||||
export type AppPlugins = Pick<ShimPlugins, 'data' | 'management'>;
|
||||
|
||||
export interface AppDependencies {
|
||||
core: AppCore;
|
||||
|
@ -60,7 +59,6 @@ export interface ShimPlugins extends NpPlugins {
|
|||
uiMetric: {
|
||||
createUiStatsReporter: typeof createUiStatsReporter;
|
||||
};
|
||||
xsrfToken: string;
|
||||
}
|
||||
|
||||
export function createPublicShim(): { core: ShimCore; plugins: ShimPlugins } {
|
||||
|
@ -88,7 +86,6 @@ export function createPublicShim(): { core: ShimCore; plugins: ShimPlugins } {
|
|||
uiMetric: {
|
||||
createUiStatsReporter,
|
||||
},
|
||||
xsrfToken: chrome.getXsrfToken(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue