Skip to content

Commit

Permalink
Index Management new platform migration (elastic#49359) (elastic#50812)
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth authored Nov 15, 2019
1 parent 3a07881 commit f530fec
Show file tree
Hide file tree
Showing 179 changed files with 1,325 additions and 769 deletions.
166 changes: 166 additions & 0 deletions src/plugins/es_ui_shared/public/request/np_ready_request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* 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 { useEffect, useState, useRef } from 'react';

import { HttpServiceBase } from '../../../../../src/core/public';

export interface SendRequestConfig {
path: string;
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head';
body?: any;
}

export interface SendRequestResponse {
data: any;
error: Error | null;
}

export interface UseRequestConfig extends SendRequestConfig {
pollIntervalMs?: number;
initialData?: any;
deserializer?: (data: any) => any;
}

export interface UseRequestResponse {
isInitialRequest: boolean;
isLoading: boolean;
error: null | unknown;
data: any;
sendRequest: (...args: any[]) => Promise<SendRequestResponse>;
}

export const sendRequest = async (
httpClient: HttpServiceBase,
{ path, method, body }: SendRequestConfig
): Promise<SendRequestResponse> => {
try {
const response = await httpClient[method](path, { body });

return {
data: response.data ? response.data : response,
error: null,
};
} catch (e) {
return {
data: null,
error: e.response && e.response.data ? e.response.data : e.body,
};
}
};

export const useRequest = (
httpClient: HttpServiceBase,
{
path,
method,
body,
pollIntervalMs,
initialData,
deserializer = (data: any): any => data,
}: UseRequestConfig
): UseRequestResponse => {
// Main states for tracking request status and data
const [error, setError] = useState<null | any>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [data, setData] = useState<any>(initialData);

// Consumers can use isInitialRequest to implement a polling UX.
const [isInitialRequest, setIsInitialRequest] = useState<boolean>(true);
const pollInterval = useRef<any>(null);
const pollIntervalId = useRef<any>(null);

// We always want to use the most recently-set interval in scheduleRequest.
pollInterval.current = pollIntervalMs;

// Tied to every render and bound to each request.
let isOutdatedRequest = false;

const scheduleRequest = () => {
// Clear current interval
if (pollIntervalId.current) {
clearTimeout(pollIntervalId.current);
}

// Set new interval
if (pollInterval.current) {
pollIntervalId.current = setTimeout(_sendRequest, pollInterval.current);
}
};

const _sendRequest = async () => {
// We don't clear error or data, so it's up to the consumer to decide whether to display the
// "old" error/data or loading state when a new request is in-flight.
setIsLoading(true);

const requestBody = {
path,
method,
body,
};

const response = await sendRequest(httpClient, requestBody);
const { data: serializedResponseData, error: responseError } = response;
const responseData = deserializer(serializedResponseData);

// If an outdated request has resolved, DON'T update state, but DO allow the processData handler
// to execute side effects like update telemetry.
if (isOutdatedRequest) {
return { data: null, error: null };
}

setError(responseError);
setData(responseData);
setIsLoading(false);
setIsInitialRequest(false);

// If we're on an interval, we need to schedule the next request. This also allows us to reset
// the interval if the user has manually requested the data, to avoid doubled-up requests.
scheduleRequest();

return { data: serializedResponseData, error: responseError };
};

useEffect(() => {
_sendRequest();
// To be functionally correct we'd send a new request if the method, path, or body changes.
// But it doesn't seem likely that the method will change and body is likely to be a new
// object even if its shape hasn't changed, so for now we're just watching the path.
}, [path]);

useEffect(() => {
scheduleRequest();

// Clean up intervals and inflight requests and corresponding state changes
return () => {
isOutdatedRequest = true;
if (pollIntervalId.current) {
clearTimeout(pollIntervalId.current);
}
};
}, [pollIntervalMs]);

return {
isInitialRequest,
isLoading,
error,
data,
sendRequest: _sendRequest, // Gives the user the ability to manually request data
};
};
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/cross_cluster_replication/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { PLUGIN } from './common/constants';
import { registerLicenseChecker } from './server/lib/register_license_checker';
import { registerRoutes } from './server/routes/register_routes';
import { ccrDataEnricher } from './cross_cluster_replication_data';
import { addIndexManagementDataEnricher } from '../index_management/index_management_data';
import { addIndexManagementDataEnricher } from '../index_management/server/index_management_data';
export function crossClusterReplication(kibana) {
return new kibana.Plugin({
id: PLUGIN.ID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@kbn/i18n/react';
import { getIndexListUri } from '../../../../../../../../index_management/public/services/navigation';
import { getIndexListUri } from '../../../../../../../../index_management/public/app/services/navigation';

import {
EuiButton,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@kbn/i18n/react';
import { getIndexListUri } from '../../../../../../../../index_management/public/services/navigation';
import { getIndexListUri } from '../../../../../../../../index_management/public/app/services/navigation';

import {
EuiButton,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
} from '@elastic/eui';
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';

import { getIndexListUri } from '../../../../../../index_management/public/services/navigation';
import { getIndexListUri } from '../../../../../../index_management/public/app/services/navigation';
import { BASE_PATH, UIM_EDIT_CLICK } from '../../../../../common/constants';
import { getPolicyPath } from '../../../../services/navigation';
import { flattenPanelTree } from '../../../../services/flatten_panel_tree';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
findTestSubject,
nextTick,
} from '../../../../../../test_utils';
import { IndexManagementHome } from '../../../public/sections/home';
import { IndexManagementHome } from '../../../public/app/sections/home';
import { BASE_PATH } from '../../../common/constants';
import { indexManagementStore } from '../../../public/store';
import { indexManagementStore } from '../../../public/app/store';
import { Template } from '../../../common/types';

const testBedConfig: TestBedConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,30 @@
*/

import sinon, { SinonFakeServer } from 'sinon';

const API_PATH = '/api/index_management';
import { API_BASE_PATH } from '../../../common/constants';

type HttpResponse = Record<string, any> | any[];

// Register helpers to mock HTTP Requests
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadTemplatesResponse = (response: HttpResponse = []) => {
server.respondWith('GET', `${API_PATH}/templates`, [
server.respondWith('GET', `${API_BASE_PATH}/templates`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setLoadIndicesResponse = (response: HttpResponse = []) => {
server.respondWith('GET', `${API_PATH}/indices`, [
server.respondWith('GET', `${API_BASE_PATH}/indices`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setDeleteTemplateResponse = (response: HttpResponse = []) => {
server.respondWith('DELETE', `${API_PATH}/templates`, [
server.respondWith('DELETE', `${API_BASE_PATH}/templates`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
Expand All @@ -40,18 +39,18 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const status = error ? error.status || 400 : 200;
const body = error ? error.body : response;

server.respondWith('GET', `${API_PATH}/templates/:id`, [
server.respondWith('GET', `${API_BASE_PATH}/templates/:id`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),
]);
};

const setCreateTemplateResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const status = error ? error.body.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('PUT', `${API_PATH}/templates`, [
server.respondWith('PUT', `${API_BASE_PATH}/templates`, [
status,
{ 'Content-Type': 'application/json' },
body,
Expand All @@ -62,7 +61,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const status = error ? error.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('PUT', `${API_PATH}/templates/:name`, [
server.respondWith('PUT', `${API_BASE_PATH}/templates/:name`, [
status,
{ 'Content-Type': 'application/json' },
body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,30 @@
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import { init as initHttpRequests } from './http_requests';
import { setHttpClient } from '../../../public/services/api';
import { httpService } from '../../../public/app/services/http';
import { breadcrumbService } from '../../../public/app/services/breadcrumbs';
import { documentationService } from '../../../public/app/services/documentation';
import { notificationService } from '../../../public/app/services/notification';
import { uiMetricService } from '../../../public/app/services/ui_metric';
import { createUiStatsReporter } from '../../../../../../../src/legacy/core_plugins/ui_metric/public';

/* eslint-disable @kbn/eslint/no-restricted-paths */
import { notificationServiceMock } from '../../../../../../../src/core/public/notifications/notifications_service.mock';
import { chromeServiceMock } from '../../../../../../../src/core/public/chrome/chrome_service.mock';
import { docLinksServiceMock } from '../../../../../../../src/core/public/doc_links/doc_links_service.mock';

const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });

export const setupEnvironment = () => {
const { server, httpRequestsMockHelpers } = initHttpRequests();

// Mock initialization of services
// @ts-ignore
setHttpClient(mockHttpClient);
httpService.init(mockHttpClient);
breadcrumbService.init(chromeServiceMock.createStartContract(), '');
documentationService.init(docLinksServiceMock.createStartContract());
notificationService.init(notificationServiceMock.createStartContract());
uiMetricService.init(createUiStatsReporter);

const { server, httpRequestsMockHelpers } = initHttpRequests();

return {
server,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
import { BASE_PATH } from '../../../common/constants';
import { TemplateClone } from '../../../public/sections/template_clone';
import { TemplateClone } from '../../../public/app/sections/template_clone';
import { formSetup } from './template_form.helpers';
import { TEMPLATE_NAME } from './constants';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
import { BASE_PATH } from '../../../common/constants';
import { TemplateCreate } from '../../../public/sections/template_create';
import { TemplateCreate } from '../../../public/app/sections/template_create';
import { formSetup, TestSubjects } from './template_form.helpers';

const testBedConfig: TestBedConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
import { BASE_PATH } from '../../../common/constants';
import { TemplateEdit } from '../../../public/sections/template_edit';
import { TemplateEdit } from '../../../public/app/sections/template_edit';
import { formSetup, TestSubjects } from './template_form.helpers';
import { TEMPLATE_NAME } from './constants';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { act } from 'react-dom/test-utils';
import * as fixtures from '../../test/fixtures';
import { setupEnvironment, pageHelpers, nextTick, getRandomString } from './helpers';
import { IdxMgmtHomeTestBed } from './helpers/home.helpers';

const API_PATH = '/api/index_management';
import { API_BASE_PATH } from '../../common/constants';

const { setup } = pageHelpers.home;

Expand All @@ -21,16 +20,7 @@ const removeWhiteSpaceOnArrayValues = (array: any[]) =>
return value.trim();
});

jest.mock('ui/index_patterns', () => ({
ILLEGAL_CHARACTERS: '',
CONTAINS_SPACES: '',
validateIndexPattern: () => {},
}));

jest.mock('ui/chrome', () => ({
breadcrumbs: { set: () => {} },
addBasePath: (path: string) => path || '/api/index_management',
}));
jest.mock('ui/new_platform');

// We need to skip the tests until react 16.9.0 is released
// which supports asynchronous code inside act()
Expand Down Expand Up @@ -204,7 +194,9 @@ describe.skip('<IndexManagementHome />', () => {
});

expect(server.requests.length).toBe(totalRequests + 1);
expect(server.requests[server.requests.length - 1].url).toBe(`${API_PATH}/templates`);
expect(server.requests[server.requests.length - 1].url).toBe(
`${API_BASE_PATH}/templates`
);
});

test('should have a button to create a new template', () => {
Expand Down Expand Up @@ -346,7 +338,7 @@ describe.skip('<IndexManagementHome />', () => {
const latestRequest = server.requests[server.requests.length - 1];

expect(latestRequest.method).toBe('DELETE');
expect(latestRequest.url).toBe(`${API_PATH}/templates/${template1.name}`);
expect(latestRequest.url).toBe(`${API_BASE_PATH}/templates/${template1.name}`);
});
});

Expand Down
Loading

0 comments on commit f530fec

Please sign in to comment.