Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/ilm_typescript' into ilm_node_de…
Browse files Browse the repository at this point in the history
…tails_typescript

# Conflicts:
#	x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation/node_allocation.js
#	x-pack/plugins/index_lifecycle_management/public/application/store/actions/nodes.js
#	x-pack/plugins/index_lifecycle_management/public/application/store/reducers/nodes.js
#	x-pack/plugins/translations/translations/ja-JP.json
#	x-pack/plugins/translations/translations/zh-CN.json
  • Loading branch information
yuliacech committed Jul 31, 2020
2 parents b380789 + 739be48 commit 32a4dba
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 184 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 sinon, { SinonFakeServer } from 'sinon';

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

const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setPoliciesResponse = (response: HttpResponse = []) => {
server.respondWith('/api/index_lifecycle_management/policies', [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setNodesListResponse = (response: HttpResponse = []) => {
server.respondWith('/api/index_lifecycle_management/nodes/list', [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

return {
setPoliciesResponse,
setNodesListResponse,
};
};

export const init = () => {
const server = sinon.fakeServer.create();

// Define default response for unhandled requests.
// We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry,
// and we can mock them all with a 200 instead of mocking each one individually.
server.respondWith([200, {}, 'DefaultSinonMockServerResponse']);

const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server);

return {
server,
httpRequestsMockHelpers,
};
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 React, { ReactNode } from 'react';
import { EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

import { createDocLink } from '../../services/documentation';

interface Props {
docPath: string;
text?: ReactNode;
}

export const LearnMoreLink: React.FunctionComponent<Props> = ({ docPath, text }) => {
const content = text ? (
text
) : (
<FormattedMessage id="xpack.indexLifecycleMgmt.learnMore" defaultMessage="Learn more" />
);
return (
<EuiLink href={createDocLink(docPath)} target="_blank" external={true}>
{content}
</EuiLink>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { NodeAllocation } from './node_allocation.container';
export { NodeAllocation } from './node_allocation';

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* 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 React, { Fragment, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
EuiSelect,
EuiButtonEmpty,
EuiCallOut,
EuiSpacer,
EuiLoadingSpinner,
EuiButton,
} from '@elastic/eui';

import { PHASE_NODE_ATTRS } from '../../../../constants';
import { LearnMoreLink } from '../../../components/learn_more_link';
import { ErrableFormRow } from '../../form_errors';
import { useLoadNodes } from '../../../../services/api';
import { NodeAttrsDetails } from '../node_attrs_details';

interface Props {
phase: string;
setPhaseData: (dataKey: string, value: any) => void;
errors: any;
phaseData: any;
isShowingErrors: boolean;
}

const learnMoreLink = (
<Fragment>
<EuiSpacer size="m" />
<LearnMoreLink
text={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.learnAboutShardAllocationLink"
defaultMessage="Learn about shard allocation"
/>
}
docPath="modules-cluster.html#cluster-shard-allocation-settings"
/>
</Fragment>
);

export const NodeAllocation: React.FunctionComponent<Props> = ({
phase,
setPhaseData,
errors,
phaseData,
isShowingErrors,
}) => {
const { isLoading, data: nodes, error, sendRequest } = useLoadNodes();

const { selectedNodeAttrsForDetails, setSelectedNodeAttrsForDetails } = useState(null);

if (isLoading) {
return (
<Fragment>
<EuiLoadingSpinner size="xl" />
<EuiSpacer size="m" />
</Fragment>
);
}

if (error) {
const { error: errorString, statusCode, message } = error;
return (
<Fragment>
<EuiCallOut
style={{ maxWidth: 400 }}
title={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle"
defaultMessage="Unable to load node attributes."
/>
}
color="danger"
>
<p>
{statusCode}: {errorString}. {message}
</p>
<EuiButton onClick={sendRequest} iconType="refresh" color="danger">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.nodeAttributesReloadButton"
defaultMessage="Try again"
/>
</EuiButton>
</EuiCallOut>

<EuiSpacer size="xl" />
</Fragment>
);
}

let nodeOptions = Object.keys(nodes).map((attrs) => ({
text: `${attrs} (${nodes[attrs].length})`,
value: attrs,
}));

nodeOptions.sort((a, b) => a.value.localeCompare(b.value));
if (nodeOptions.length) {
nodeOptions = [
{
text: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultNodeAllocation', {
defaultMessage: "Default allocation (don't use attributes)",
}),
value: '',
},
...nodeOptions,
];
}
if (!nodeOptions.length) {
return (
<Fragment>
<EuiCallOut
style={{ maxWidth: 400 }}
title={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel"
defaultMessage="No node attributes configured in elasticsearch.yml"
/>
}
color="warning"
>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingDescription"
defaultMessage="You can't control shard allocation without node attributes."
/>
{learnMoreLink}
</EuiCallOut>

<EuiSpacer size="xl" />
</Fragment>
);
}

return (
<Fragment>
<ErrableFormRow
id={`${phase}-${PHASE_NODE_ATTRS}`}
label={i18n.translate('xpack.indexLifecycleMgmt.editPolicy.nodeAllocationLabel', {
defaultMessage: 'Select a node attribute to control shard allocation',
})}
errorKey={PHASE_NODE_ATTRS}
isShowingErrors={isShowingErrors}
errors={errors}
>
<EuiSelect
id={`${phase}-${PHASE_NODE_ATTRS}`}
value={phaseData[PHASE_NODE_ATTRS] || ' '}
options={nodeOptions}
onChange={(e) => {
setPhaseData(PHASE_NODE_ATTRS, e.target.value);
}}
/>
</ErrableFormRow>
{!!phaseData[PHASE_NODE_ATTRS] ? (
<EuiButtonEmpty
style={{ maxWidth: 400 }}
data-test-subj={`${phase}-viewNodeDetailsFlyoutButton`}
flush="left"
iconType="eye"
onClick={() => setSelectedNodeAttrsForDetails(phaseData[PHASE_NODE_ATTRS])}
>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.viewNodeDetailsButton"
defaultMessage="View a list of nodes attached to this configuration"
/>
</EuiButtonEmpty>
) : null}
{learnMoreLink}
<EuiSpacer size="m" />

{selectedNodeAttrsForDetails ? (
<NodeAttrsDetails
selectedNodeAttrs={selectedNodeAttrsForDetails}
close={() => setSelectedNodeAttrsForDetails(null)}
/>
) : null}
</Fragment>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { cloneElement, Children, Fragment } from 'react';
import { EuiFormRow } from '@elastic/eui';
import React, { cloneElement, Children, Fragment, ReactElement } from 'react';
import { EuiFormRow, EuiFormRowProps } from '@elastic/eui';

export const ErrableFormRow = ({ errorKey, isShowingErrors, errors, children, ...rest }) => {
type Props = EuiFormRowProps & {
errorKey: string;
isShowingErrors: boolean;
errors: Record<string, string[]>;
};

export const ErrableFormRow: React.FunctionComponent<Props> = ({
errorKey,
isShowingErrors,
errors,
children,
...rest
}) => {
return (
<EuiFormRow
isInvalid={isShowingErrors && errors[errorKey].length > 0}
Expand All @@ -16,7 +28,7 @@ export const ErrableFormRow = ({ errorKey, isShowingErrors, errors, children, ..
>
<Fragment>
{Children.map(children, (child) =>
cloneElement(child, {
cloneElement(child as ReactElement, {
isInvalid: isShowingErrors && errors[errorKey].length > 0,
})
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ interface GenericObject {
[key: string]: any;
}

export async function loadNodes() {
return await sendGet(`nodes/list`);
}
export const useLoadNodes = () => {
return useRequest({
path: `nodes/list`,
method: 'get',
initialData: [],
});
};

export const useLoadNodeDetails = (selectedNodeAttrs: string) => {
return useRequest({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { HttpSetup } from 'src/core/public';
import {
UseRequestConfig,
useRequest as _useRequest,
Error,
} from '../../../../../../src/plugins/es_ui_shared/public';

interface GenericObject {
Expand Down Expand Up @@ -43,6 +42,8 @@ export function sendDelete(path: string) {
return _httpClient.delete(getFullPath(path));
}

export const useRequest = (config: UseRequestConfig) => {
return _useRequest<any, Error>(_httpClient, { ...config, path: getFullPath(config.path) });
export const useRequest = <T = any, E = { statusCode: number; error: string; message: string }>(
config: UseRequestConfig
) => {
return _useRequest<T, E>(_httpClient, { ...config, path: getFullPath(config.path) });
};
Loading

0 comments on commit 32a4dba

Please sign in to comment.