Skip to content

Commit

Permalink
[ML] Transforms: NP ui/imports migration (#58469) (#58868)
Browse files Browse the repository at this point in the history
- Migrates ui/* imports to NP.
- Uses direct React mounting instead of Angular.
- Service Cleanup.
  • Loading branch information
walterra authored Feb 28, 2020
1 parent 7cf25b9 commit e67873f
Show file tree
Hide file tree
Showing 49 changed files with 423 additions and 545 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/

jest.mock('ui/new_platform');

export function XJsonMode() {}
export function setDependencyCache() {}
export const useRequest = () => ({
isLoading: false,
error: null,
data: undefined,
});
export { mlInMemoryTableBasicFactory } from '../../../ml/public/application/components/ml_in_memory_table';
export const SORT_DIRECTION = { ASC: 'asc' };
49 changes: 27 additions & 22 deletions x-pack/legacy/plugins/transform/public/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import React, { useContext, FC } from 'react';
import { render } from 'react-dom';
import { Redirect, Route, Switch } from 'react-router-dom';
import { render, unmountComponentAtNode } from 'react-dom';
import { HashRouter, Redirect, Route, Switch } from 'react-router-dom';

import { FormattedMessage } from '@kbn/i18n/react';

Expand All @@ -22,8 +22,7 @@ import { TransformManagementSection } from './sections/transform_management';

export const App: FC = () => {
const { apiError } = useContext(AuthorizationContext);

if (apiError) {
if (apiError !== null) {
return (
<SectionError
title={
Expand All @@ -39,33 +38,39 @@ export const App: FC = () => {

return (
<div data-test-subj="transformApp">
<Switch>
<Route
path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CLONE_TRANSFORM}/:transformId`}
component={CloneTransformSection}
/>
<Route
path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CREATE_TRANSFORM}/:savedObjectId`}
component={CreateTransformSection}
/>
<Route
exact
path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`}
component={TransformManagementSection}
/>
<Redirect from={`${CLIENT_BASE_PATH}`} to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`} />
</Switch>
<HashRouter>
<Switch>
<Route
path={`${CLIENT_BASE_PATH}${SECTION_SLUG.CLONE_TRANSFORM}/:transformId`}
component={CloneTransformSection}
/>
<Route
path={`${CLIENT_BASE_PATH}${SECTION_SLUG.CREATE_TRANSFORM}/:savedObjectId`}
component={CreateTransformSection}
/>
<Route
exact
path={CLIENT_BASE_PATH + SECTION_SLUG.HOME}
component={TransformManagementSection}
/>
<Redirect from={CLIENT_BASE_PATH} to={CLIENT_BASE_PATH + SECTION_SLUG.HOME} />
</Switch>
</HashRouter>
</div>
);
};

export const renderReact = (elem: Element, appDependencies: AppDependencies) => {
export const renderApp = (element: HTMLElement, appDependencies: AppDependencies) => {
const Providers = getAppProviders(appDependencies);

render(
<Providers>
<App />
</Providers>,
elem
element
);

return () => {
unmountComponentAtNode(element);
};
};
43 changes: 25 additions & 18 deletions x-pack/legacy/plugins/transform/public/app/app_dependencies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,27 @@
import React, { createContext, useContext, ReactNode } from 'react';
import { HashRouter } from 'react-router-dom';

import chrome from 'ui/chrome';
import { metadata } from 'ui/metadata';

import { API_BASE_PATH } from '../../common/constants';

import { setDependencyCache } from '../shared_imports';
import { AppDependencies } from '../shim';

import { AuthorizationProvider } from './lib/authorization';

const legacyBasePath = {
prepend: chrome.addBasePath,
get: chrome.getBasePath,
remove: () => {},
};
const legacyDocLinks = {
ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
DOC_LINK_VERSION: metadata.branch,
};

let DependenciesContext: React.Context<AppDependencies>;

const setAppDependencies = (deps: AppDependencies) => {
const legacyBasePath = {
prepend: deps.core.http.basePath.prepend,
get: deps.core.http.basePath.get,
remove: () => {},
};

setDependencyCache({
autocomplete: deps.plugins.data.autocomplete,
docLinks: legacyDocLinks as any,
docLinks: deps.core.docLinks,
basePath: legacyBasePath as any,
XSRF: chrome.getXsrfToken(),
XSRF: deps.plugins.xsrfToken,
});
DependenciesContext = createContext<AppDependencies>(deps);
return DependenciesContext.Provider;
Expand All @@ -48,16 +41,30 @@ export const useAppDependencies = () => {
return useContext<AppDependencies>(DependenciesContext);
};

export const useDocumentationLinks = () => {
const {
core: { documentation },
} = useAppDependencies();
return documentation;
};

export const useToastNotifications = () => {
const {
core: {
notifications: { toasts: toastNotifications },
},
} = useAppDependencies();
return toastNotifications;
};

export const getAppProviders = (deps: AppDependencies) => {
const I18nContext = deps.core.i18n.Context;

// Create App dependencies context and get its provider
const AppDependenciesProvider = setAppDependencies(deps);

return ({ children }: { children: ReactNode }) => (
<AuthorizationProvider
privilegesEndpoint={deps.core.http.basePath.prepend(`${API_BASE_PATH}privileges`)}
>
<AuthorizationProvider privilegesEndpoint={`${API_BASE_PATH}privileges`}>
<I18nContext>
<HashRouter>
<AppDependenciesProvider value={deps}>{children}</AppDependenciesProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export function getDiscoverUrl(indexPatternId: string, baseUrl: string): string
}

export const RedirectToTransformManagement: FC = () => (
<Redirect from={`${CLIENT_BASE_PATH}`} to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.HOME}`} />
<Redirect from={CLIENT_BASE_PATH} to={CLIENT_BASE_PATH + SECTION_SLUG.HOME} />
);

export const RedirectToCreateTransform: FC<{ savedObjectId: string }> = ({ savedObjectId }) => (
<Redirect to={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CREATE_TRANSFORM}/${savedObjectId}`} />
<Redirect to={`${CLIENT_BASE_PATH}${SECTION_SLUG.CREATE_TRANSFORM}/${savedObjectId}`} />
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import React, { Fragment } from 'react';
import { EuiCallOut } from '@elastic/eui';
import React from 'react';

interface Props {
title: React.ReactNode;
error: {
data: {
error: string;
cause?: string[];
message?: string;
};
};
error: Error | null;
actions?: JSX.Element;
}

Expand All @@ -25,25 +19,11 @@ export const SectionError: React.FunctionComponent<Props> = ({
actions,
...rest
}) => {
const {
error: errorString,
cause, // wrapEsError() on the server adds a "cause" array
message,
} = error.data;
const errorMessage = error?.message ?? JSON.stringify(error, null, 2);

return (
<EuiCallOut title={title} color="danger" iconType="alert" {...rest}>
{cause ? message || errorString : <p>{message || errorString}</p>}
{cause && (
<Fragment>
<EuiSpacer size="s" />
<ul>
{cause.map((causeMsg, i) => (
<li key={i}>{causeMsg}</li>
))}
</ul>
</Fragment>
)}
<pre>{errorMessage}</pre>
{actions ? actions : null}
</EuiCallOut>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,44 @@

import React from 'react';
import { render } from '@testing-library/react';

import { KibanaContext } from '../lib/kibana';
import { createPublicShim } from '../../shim';
import { getAppProviders } from '../app_dependencies';

import { ToastNotificationText } from './toast_notification_text';

jest.mock('../../shared_imports');

describe('ToastNotificationText', () => {
test('should render the text as plain text', () => {
const Providers = getAppProviders(createPublicShim());
const props = {
text: 'a short text message',
};
const { container } = render(<ToastNotificationText {...props} />);
const { container } = render(
<Providers>
<KibanaContext.Provider value={{ initialized: false }}>
<ToastNotificationText {...props} />
</KibanaContext.Provider>
</Providers>
);
expect(container.textContent).toBe('a short text message');
});

test('should render the text within a modal', () => {
const Providers = getAppProviders(createPublicShim());
const props = {
text:
'a text message that is longer than 140 characters. a text message that is longer than 140 characters. a text message that is longer than 140 characters. ',
};
const { container } = render(<ToastNotificationText {...props} />);
const { container } = render(
<Providers>
<KibanaContext.Provider value={{ initialized: false }}>
<ToastNotificationText {...props} />
</KibanaContext.Provider>
</Providers>
);
expect(container.textContent).toBe(
'a text message that is longer than 140 characters. a text message that is longer than 140 characters. a text message that is longer than 140 ...View details'
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ import {

import { i18n } from '@kbn/i18n';

import { npStart } from 'ui/new_platform';
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';

import { useAppDependencies } from '../app_dependencies';

const MAX_SIMPLE_MESSAGE_LENGTH = 140;

export const ToastNotificationText: FC<{ text: any }> = ({ text }) => {
const {
core: { overlays },
} = useAppDependencies();

if (typeof text === 'string' && text.length <= MAX_SIMPLE_MESSAGE_LENGTH) {
return text;
}
Expand All @@ -43,7 +48,7 @@ export const ToastNotificationText: FC<{ text: any }> = ({ text }) => {
}`;

const openModal = () => {
const modal = npStart.core.overlays.openModal(
const modal = overlays.openModal(
toMountPoint(
<EuiModal onClose={() => modal.close()}>
<EuiModalHeader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const CLIENT_BASE_PATH = '/management/elasticsearch/transform';
export const CLIENT_BASE_PATH = '/management/elasticsearch/transform/';

export enum SECTION_SLUG {
HOME = 'transform_management',
Expand Down
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/transform/public/app/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { useGetTransforms } from './use_get_transforms';
export { useDeleteTransforms } from './use_delete_transform';
export { useStartTransforms } from './use_start_transform';
export { useStopTransforms } from './use_stop_transform';
export { useRequest } from './use_request';
10 changes: 5 additions & 5 deletions x-pack/legacy/plugins/transform/public/app/hooks/use_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
*/

import { useAppDependencies } from '../app_dependencies';

import { PreviewRequestBody, TransformId } from '../common';

import { http } from '../services/http_service';
import { httpFactory, Http } from '../services/http_service';

import { EsIndex, TransformEndpointRequest, TransformEndpointResult } from './use_api_types';

const apiFactory = (basePath: string, indicesBasePath: string) => ({
const apiFactory = (basePath: string, indicesBasePath: string, http: Http) => ({
getTransforms(transformId?: TransformId): Promise<any> {
const transformIdString = transformId !== undefined ? `/${transformId}` : '';
return http({
Expand Down Expand Up @@ -98,6 +96,8 @@ export const useApi = () => {

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);

return apiFactory(basePath, indicesBasePath);
return apiFactory(basePath, indicesBasePath, http);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
import React from 'react';

import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';

import { useToastNotifications } from '../app_dependencies';
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
import { ToastNotificationText } from '../components';

import { useApi } from './use_api';
import { TransformEndpointRequest, TransformEndpointResult } from './use_api_types';

export const useDeleteTransforms = () => {
const toastNotifications = useToastNotifications();
const api = useApi();

return async (transforms: TransformListRow[]) => {
Expand Down Expand Up @@ -54,7 +55,9 @@ export const useDeleteTransforms = () => {
title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
}),
text: toMountPoint(<ToastNotificationText text={e} />),
text: toMountPoint(
<ToastNotificationText text={e?.message ?? JSON.stringify(e, null, 2)} />
),
});
}
};
Expand Down
16 changes: 16 additions & 0 deletions x-pack/legacy/plugins/transform/public/app/hooks/use_request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { UseRequestConfig, useRequest as _useRequest } from '../../shared_imports';

import { useAppDependencies } from '../app_dependencies';

export const useRequest = (config: UseRequestConfig) => {
const {
core: { http },
} = useAppDependencies();
return _useRequest(http, config);
};
Loading

0 comments on commit e67873f

Please sign in to comment.