mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Add modal with details to shard error toast (#41649)
* Add details to shard error toast * Implement failure modal with lots of details * Add jest tests * Update core API change because of the new modal Prop 'className'
This commit is contained in:
parent
5bd37d0c02
commit
e452f436e3
22 changed files with 1304 additions and 8 deletions
|
@ -77,6 +77,7 @@ export interface OverlayStart {
|
|||
openModal: (
|
||||
modalChildren: React.ReactNode,
|
||||
modalProps?: {
|
||||
className?: string;
|
||||
closeButtonAriaLabel?: string;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
|
|
@ -545,6 +545,7 @@ export interface OverlayStart {
|
|||
}) => OverlayRef;
|
||||
// (undocumented)
|
||||
openModal: (modalChildren: React.ReactNode, modalProps?: {
|
||||
className?: string;
|
||||
closeButtonAriaLabel?: string;
|
||||
'data-test-subj'?: string;
|
||||
}) => OverlayRef;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
@import './accessibility/index';
|
||||
@import './chrome/index';
|
||||
@import './courier/index';
|
||||
@import './collapsible_sidebar/index';
|
||||
@import './directives/index';
|
||||
@import './error_allow_explicit_index/index';
|
||||
|
|
1
src/legacy/ui/public/courier/_index.scss
Normal file
1
src/legacy/ui/public/courier/_index.scss
Normal file
|
@ -0,0 +1 @@
|
|||
@import './fetch/components/shard_failure_modal';
|
|
@ -16,12 +16,14 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { toastNotifications } from '../../notify';
|
||||
import { RequestFailure } from '../../errors';
|
||||
import { RequestStatus } from './req_status';
|
||||
import { SearchError } from '../search_strategy/search_error';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ShardFailureOpenModalButton } from './components/shard_failure_open_modal_button';
|
||||
|
||||
export function CallResponseHandlersProvider(Promise) {
|
||||
const ABORTED = RequestStatus.ABORTED;
|
||||
|
@ -39,16 +41,37 @@ export function CallResponseHandlersProvider(Promise) {
|
|||
toastNotifications.addWarning({
|
||||
title: i18n.translate('common.ui.courier.fetch.requestTimedOutNotificationMessage', {
|
||||
defaultMessage: 'Data might be incomplete because your request timed out',
|
||||
})
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (response._shards && response._shards.failed) {
|
||||
const title = i18n.translate('common.ui.courier.fetch.shardsFailedNotificationMessage', {
|
||||
defaultMessage: '{shardsFailed} of {shardsTotal} shards failed',
|
||||
values: {
|
||||
shardsFailed: response._shards.failed,
|
||||
shardsTotal: response._shards.total,
|
||||
},
|
||||
});
|
||||
const description = i18n.translate('common.ui.courier.fetch.shardsFailedNotificationDescription', {
|
||||
defaultMessage: 'The data you are seeing might be incomplete or wrong.',
|
||||
});
|
||||
|
||||
const text = (
|
||||
<>
|
||||
{description}
|
||||
<EuiSpacer size="s"/>
|
||||
<ShardFailureOpenModalButton
|
||||
request={searchRequest.fetchParams.body}
|
||||
response={response}
|
||||
title={title}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
toastNotifications.addWarning({
|
||||
title: i18n.translate('common.ui.courier.fetch.shardsFailedNotificationMessage', {
|
||||
defaultMessage: '{shardsFailed} of {shardsTotal} shards failed',
|
||||
values: { shardsFailed: response._shards.failed, shardsTotal: response._shards.total }
|
||||
})
|
||||
title,
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -65,7 +88,11 @@ export function CallResponseHandlersProvider(Promise) {
|
|||
if (searchRequest.filterError(response)) {
|
||||
return progress();
|
||||
} else {
|
||||
return searchRequest.handleFailure(response.error instanceof SearchError ? response.error : new RequestFailure(null, response));
|
||||
return searchRequest.handleFailure(
|
||||
response.error instanceof SearchError
|
||||
? response.error
|
||||
: new RequestFailure(null, response)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { Request } from '../shard_failure_types';
|
||||
export const shardFailureRequest = {
|
||||
version: true,
|
||||
size: 500,
|
||||
sort: [],
|
||||
_source: {
|
||||
excludes: [],
|
||||
},
|
||||
stored_fields: ['*'],
|
||||
script_fields: {},
|
||||
docvalue_fields: [],
|
||||
query: {},
|
||||
highlight: {},
|
||||
} as Request;
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 { ResponseWithShardFailure } from '../shard_failure_types';
|
||||
|
||||
export const shardFailureResponse = {
|
||||
_shards: {
|
||||
total: 2,
|
||||
successful: 1,
|
||||
skipped: 0,
|
||||
failed: 1,
|
||||
failures: [
|
||||
{
|
||||
shard: 0,
|
||||
index: 'repro2',
|
||||
node: 'itsmeyournode',
|
||||
reason: {
|
||||
type: 'script_exception',
|
||||
reason: 'runtime error',
|
||||
script_stack: ["return doc['targetfield'].value;", ' ^---- HERE'],
|
||||
script: "return doc['targetfield'].value;",
|
||||
lang: 'painless',
|
||||
caused_by: {
|
||||
type: 'illegal_argument_exception',
|
||||
reason: 'Gimme reason',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as ResponseWithShardFailure;
|
192
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_description.test.tsx.snap
generated
Normal file
192
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_description.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShardFailureDescription renders matching snapshot given valid properties 1`] = `
|
||||
<div>
|
||||
<ShardFailureDescriptionHeader
|
||||
index="repro2"
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"relative": Object {
|
||||
"days": Object {
|
||||
"units": "day",
|
||||
},
|
||||
"hours": Object {
|
||||
"units": "hour",
|
||||
},
|
||||
"minutes": Object {
|
||||
"units": "minute",
|
||||
},
|
||||
"months": Object {
|
||||
"units": "month",
|
||||
},
|
||||
"seconds": Object {
|
||||
"units": "second",
|
||||
},
|
||||
"years": Object {
|
||||
"units": "year",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
node="itsmeyournode"
|
||||
reason={
|
||||
Object {
|
||||
"caused_by": Object {
|
||||
"reason": "Gimme reason",
|
||||
"type": "illegal_argument_exception",
|
||||
},
|
||||
"lang": "painless",
|
||||
"reason": "runtime error",
|
||||
"script": "return doc['targetfield'].value;",
|
||||
"script_stack": Array [
|
||||
"return doc['targetfield'].value;",
|
||||
" ^---- HERE",
|
||||
],
|
||||
"type": "script_exception",
|
||||
}
|
||||
}
|
||||
shard={0}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiDescriptionList
|
||||
className="shardFailureModal__desc"
|
||||
compressed={true}
|
||||
descriptionProps={
|
||||
Object {
|
||||
"className": "shardFailureModal__descValue",
|
||||
}
|
||||
}
|
||||
listItems={
|
||||
Array [
|
||||
Object {
|
||||
"description": "script_exception",
|
||||
"title": "Type",
|
||||
},
|
||||
Object {
|
||||
"description": "runtime error",
|
||||
"title": "Reason",
|
||||
},
|
||||
Object {
|
||||
"description": <EuiCodeBlock
|
||||
isCopyable={true}
|
||||
language="java"
|
||||
paddingSize="s"
|
||||
>
|
||||
return doc['targetfield'].value;
|
||||
^---- HERE
|
||||
</EuiCodeBlock>,
|
||||
"title": "Script stack",
|
||||
},
|
||||
Object {
|
||||
"description": <EuiCodeBlock
|
||||
isCopyable={true}
|
||||
language="java"
|
||||
paddingSize="s"
|
||||
>
|
||||
return doc['targetfield'].value;
|
||||
</EuiCodeBlock>,
|
||||
"title": "Script",
|
||||
},
|
||||
Object {
|
||||
"description": "painless",
|
||||
"title": "Lang",
|
||||
},
|
||||
Object {
|
||||
"description": "illegal_argument_exception",
|
||||
"title": "Caused by type",
|
||||
},
|
||||
Object {
|
||||
"description": "Gimme reason",
|
||||
"title": "Caused by reason",
|
||||
},
|
||||
]
|
||||
}
|
||||
titleProps={
|
||||
Object {
|
||||
"className": "shardFailureModal__descTitle",
|
||||
}
|
||||
}
|
||||
type="column"
|
||||
/>
|
||||
</div>
|
||||
`;
|
194
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_modal.test.tsx.snap
generated
Normal file
194
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_modal.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,194 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShardFailureModal renders matching snapshot given valid properties 1`] = `
|
||||
<Fragment>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>
|
||||
test
|
||||
</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiTabbedContent
|
||||
autoFocus="selected"
|
||||
initialSelectedTab={
|
||||
Object {
|
||||
"content": <ShardFailureTable
|
||||
failures={
|
||||
Array [
|
||||
Object {
|
||||
"index": "repro2",
|
||||
"node": "itsmeyournode",
|
||||
"reason": Object {
|
||||
"caused_by": Object {
|
||||
"reason": "Gimme reason",
|
||||
"type": "illegal_argument_exception",
|
||||
},
|
||||
"lang": "painless",
|
||||
"reason": "runtime error",
|
||||
"script": "return doc['targetfield'].value;",
|
||||
"script_stack": Array [
|
||||
"return doc['targetfield'].value;",
|
||||
" ^---- HERE",
|
||||
],
|
||||
"type": "script_exception",
|
||||
},
|
||||
"shard": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>,
|
||||
"id": "table",
|
||||
"name": "Shard failures",
|
||||
}
|
||||
}
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"content": <ShardFailureTable
|
||||
failures={
|
||||
Array [
|
||||
Object {
|
||||
"index": "repro2",
|
||||
"node": "itsmeyournode",
|
||||
"reason": Object {
|
||||
"caused_by": Object {
|
||||
"reason": "Gimme reason",
|
||||
"type": "illegal_argument_exception",
|
||||
},
|
||||
"lang": "painless",
|
||||
"reason": "runtime error",
|
||||
"script": "return doc['targetfield'].value;",
|
||||
"script_stack": Array [
|
||||
"return doc['targetfield'].value;",
|
||||
" ^---- HERE",
|
||||
],
|
||||
"type": "script_exception",
|
||||
},
|
||||
"shard": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>,
|
||||
"id": "table",
|
||||
"name": "Shard failures",
|
||||
},
|
||||
Object {
|
||||
"content": <EuiCodeBlock
|
||||
isCopyable={true}
|
||||
language="json"
|
||||
>
|
||||
{
|
||||
"version": true,
|
||||
"size": 500,
|
||||
"sort": [],
|
||||
"_source": {
|
||||
"excludes": []
|
||||
},
|
||||
"stored_fields": [
|
||||
"*"
|
||||
],
|
||||
"script_fields": {},
|
||||
"docvalue_fields": [],
|
||||
"query": {},
|
||||
"highlight": {}
|
||||
}
|
||||
</EuiCodeBlock>,
|
||||
"id": "json-request",
|
||||
"name": "Request",
|
||||
},
|
||||
Object {
|
||||
"content": <EuiCodeBlock
|
||||
isCopyable={true}
|
||||
language="json"
|
||||
>
|
||||
{
|
||||
"_shards": {
|
||||
"total": 2,
|
||||
"successful": 1,
|
||||
"skipped": 0,
|
||||
"failed": 1,
|
||||
"failures": [
|
||||
{
|
||||
"shard": 0,
|
||||
"index": "repro2",
|
||||
"node": "itsmeyournode",
|
||||
"reason": {
|
||||
"type": "script_exception",
|
||||
"reason": "runtime error",
|
||||
"script_stack": [
|
||||
"return doc['targetfield'].value;",
|
||||
" ^---- HERE"
|
||||
],
|
||||
"script": "return doc['targetfield'].value;",
|
||||
"lang": "painless",
|
||||
"caused_by": {
|
||||
"type": "illegal_argument_exception",
|
||||
"reason": "Gimme reason"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
</EuiCodeBlock>,
|
||||
"id": "json-response",
|
||||
"name": "Response",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiCopy
|
||||
afterMessage="Copied"
|
||||
textToCopy="{
|
||||
\\"_shards\\": {
|
||||
\\"total\\": 2,
|
||||
\\"successful\\": 1,
|
||||
\\"skipped\\": 0,
|
||||
\\"failed\\": 1,
|
||||
\\"failures\\": [
|
||||
{
|
||||
\\"shard\\": 0,
|
||||
\\"index\\": \\"repro2\\",
|
||||
\\"node\\": \\"itsmeyournode\\",
|
||||
\\"reason\\": {
|
||||
\\"type\\": \\"script_exception\\",
|
||||
\\"reason\\": \\"runtime error\\",
|
||||
\\"script_stack\\": [
|
||||
\\"return doc['targetfield'].value;\\",
|
||||
\\" ^---- HERE\\"
|
||||
],
|
||||
\\"script\\": \\"return doc['targetfield'].value;\\",
|
||||
\\"lang\\": \\"painless\\",
|
||||
\\"caused_by\\": {
|
||||
\\"type\\": \\"illegal_argument_exception\\",
|
||||
\\"reason\\": \\"Gimme reason\\"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}"
|
||||
>
|
||||
<Component />
|
||||
</EuiCopy>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-sub="closeShardFailureModal"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
onClick={[Function]}
|
||||
size="m"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Close"
|
||||
description="Closing the Modal"
|
||||
id="common.ui.courier.fetch.shardsFailedModal.close"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</Fragment>
|
||||
`;
|
77
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap
generated
Normal file
77
src/legacy/ui/public/courier/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShardFailureTable renders matching snapshot given valid properties 1`] = `
|
||||
<EuiInMemoryTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"align": "right",
|
||||
"isExpander": true,
|
||||
"render": [Function],
|
||||
"width": "40px",
|
||||
},
|
||||
Object {
|
||||
"field": "shard",
|
||||
"name": "Shard",
|
||||
"sortable": true,
|
||||
"truncateText": true,
|
||||
"width": "80px",
|
||||
},
|
||||
Object {
|
||||
"field": "index",
|
||||
"name": "Index",
|
||||
"sortable": true,
|
||||
"truncateText": true,
|
||||
},
|
||||
Object {
|
||||
"field": "node",
|
||||
"name": "Node",
|
||||
"sortable": true,
|
||||
"truncateText": true,
|
||||
},
|
||||
Object {
|
||||
"field": "reason.type",
|
||||
"name": "Reason",
|
||||
"truncateText": true,
|
||||
},
|
||||
]
|
||||
}
|
||||
executeQueryOptions={Object {}}
|
||||
itemId="id"
|
||||
itemIdToExpandedRowMap={Object {}}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"id": "0",
|
||||
"index": "repro2",
|
||||
"node": "itsmeyournode",
|
||||
"reason": Object {
|
||||
"caused_by": Object {
|
||||
"reason": "Gimme reason",
|
||||
"type": "illegal_argument_exception",
|
||||
},
|
||||
"lang": "painless",
|
||||
"reason": "runtime error",
|
||||
"script": "return doc['targetfield'].value;",
|
||||
"script_stack": Array [
|
||||
"return doc['targetfield'].value;",
|
||||
" ^---- HERE",
|
||||
],
|
||||
"type": "script_exception",
|
||||
},
|
||||
"shard": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
pagination={true}
|
||||
responsive={true}
|
||||
sorting={
|
||||
Object {
|
||||
"sort": Object {
|
||||
"direction": "desc",
|
||||
"field": "index",
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
|
@ -0,0 +1,41 @@
|
|||
// set width and height to fixed values to prevent resizing when you switch tabs
|
||||
.shardFailureModal {
|
||||
min-height: 75vh;
|
||||
width: 768px;
|
||||
}
|
||||
|
||||
.shardFailureModal__desc {
|
||||
// set for IE11, since without it depending on the content the width of the list
|
||||
// could be much higher than the available screenspace
|
||||
max-width: 686px;
|
||||
}
|
||||
|
||||
.shardFailureModal__descTitle {
|
||||
width: 20% !important;
|
||||
margin-top: $euiSizeS;
|
||||
}
|
||||
|
||||
.shardFailureModal__descValue {
|
||||
width: 80% !important;
|
||||
margin-top: $euiSizeS;
|
||||
}
|
||||
.shardFailureModal__keyValueTitle {
|
||||
padding-right: $euiSizeS;
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs','s') {
|
||||
.shardFailureModal__keyValueTitle {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shardFailureModal__descTitle {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.shardFailureModal__descValue {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { ShardFailureDescription } from './shard_failure_description';
|
||||
import { shardFailureResponse } from './__mocks__/shard_failure_response';
|
||||
import { ShardFailure } from './shard_failure_types';
|
||||
|
||||
describe('ShardFailureDescription', () => {
|
||||
it('renders matching snapshot given valid properties', () => {
|
||||
const failure = shardFailureResponse._shards.failures[0] as ShardFailure;
|
||||
const component = shallowWithIntl(<ShardFailureDescription {...failure} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiCodeBlock, EuiDescriptionList, EuiSpacer } from '@elastic/eui';
|
||||
import { ShardFailure } from './shard_failure_types';
|
||||
import { getFlattenedObject } from '../../../../../../legacy/utils/get_flattened_object';
|
||||
import { ShardFailureDescriptionHeader } from './shard_failure_description_header';
|
||||
|
||||
/**
|
||||
* Provides pretty formatting of a given key string
|
||||
* e.g. formats "this_key.is_nice" to "This key is nice"
|
||||
* @param key
|
||||
*/
|
||||
export function formatKey(key: string): string {
|
||||
const nameCapitalized = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
return nameCapitalized.replace(/[\._]/g, ' ');
|
||||
}
|
||||
/**
|
||||
* Adds a EuiCodeBlock to values of `script` and `script_stack` key
|
||||
* Values of other keys are handled a strings
|
||||
* @param value
|
||||
* @param key
|
||||
*/
|
||||
export function formatValueByKey(value: unknown, key: string): string | JSX.Element {
|
||||
if (key === 'script' || key === 'script_stack') {
|
||||
const valueScript = Array.isArray(value) ? value.join('\n') : String(value);
|
||||
return (
|
||||
<EuiCodeBlock language="java" paddingSize="s" isCopyable>
|
||||
{valueScript}
|
||||
</EuiCodeBlock>
|
||||
);
|
||||
} else {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
export function ShardFailureDescription(props: ShardFailure) {
|
||||
const flattendReason = getFlattenedObject(props.reason);
|
||||
|
||||
const listItems = Object.entries(flattendReason).map(([key, value]) => ({
|
||||
title: formatKey(key),
|
||||
description: formatValueByKey(value, key),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ShardFailureDescriptionHeader {...props} />
|
||||
<EuiSpacer size="m" />
|
||||
<EuiDescriptionList
|
||||
listItems={listItems}
|
||||
type="column"
|
||||
compressed
|
||||
className="shardFailureModal__desc"
|
||||
titleProps={{ className: 'shardFailureModal__descTitle' }}
|
||||
descriptionProps={{ className: 'shardFailureModal__descValue' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiCode, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ShardFailure } from './shard_failure_types';
|
||||
|
||||
export function getFailurePropsForSummary(
|
||||
failure: ShardFailure
|
||||
): Array<{ key: string; value: string }> {
|
||||
const failureDetailProps: Array<keyof ShardFailure> = ['shard', 'index', 'node'];
|
||||
return failureDetailProps
|
||||
.filter(key => typeof failure[key] === 'number' || typeof failure[key] === 'string')
|
||||
.map(key => ({ key, value: String(failure[key]) }));
|
||||
}
|
||||
|
||||
export function getFailureSummaryText(failure: ShardFailure, failureDetails?: string): string {
|
||||
const failureName = failure.reason.type;
|
||||
const displayDetails =
|
||||
typeof failureDetails === 'string' ? failureDetails : getFailureSummaryDetailsText(failure);
|
||||
|
||||
return i18n.translate('common.ui.courier.fetch.shardsFailedModal.failureHeader', {
|
||||
defaultMessage: '{failureName} at {failureDetails}',
|
||||
values: { failureName, failureDetails: displayDetails },
|
||||
description: 'Summary of shard failures, e.g. "IllegalArgumentException at shard 0 node xyz"',
|
||||
});
|
||||
}
|
||||
|
||||
export function getFailureSummaryDetailsText(failure: ShardFailure): string {
|
||||
return getFailurePropsForSummary(failure)
|
||||
.map(({ key, value }) => `${key}: ${value}`)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
export function ShardFailureDescriptionHeader(props: ShardFailure) {
|
||||
const failureDetails = getFailurePropsForSummary(props).map(kv => (
|
||||
<span className="shardFailureModal__keyValueTitle" key={kv.key}>
|
||||
<EuiCode>{kv.key}</EuiCode> {kv.value}
|
||||
</span>
|
||||
));
|
||||
return (
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{getFailureSummaryText(props, '')}
|
||||
{failureDetails}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { ShardFailureModal } from './shard_failure_modal';
|
||||
import { shardFailureRequest } from './__mocks__/shard_failure_request';
|
||||
import { shardFailureResponse } from './__mocks__/shard_failure_response';
|
||||
|
||||
describe('ShardFailureModal', () => {
|
||||
it('renders matching snapshot given valid properties', () => {
|
||||
const component = shallowWithIntl(
|
||||
<ShardFailureModal
|
||||
title="test"
|
||||
request={shardFailureRequest}
|
||||
response={shardFailureResponse}
|
||||
onClose={jest.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiCodeBlock,
|
||||
EuiTabbedContent,
|
||||
EuiCopy,
|
||||
EuiButton,
|
||||
EuiModalBody,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiModalFooter,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { ShardFailureTable } from './shard_failure_table';
|
||||
import { ResponseWithShardFailure, Request } from './shard_failure_types';
|
||||
|
||||
export interface Props {
|
||||
onClose: () => void;
|
||||
request: Request;
|
||||
response: ResponseWithShardFailure;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function ShardFailureModal({ request, response, title, onClose }: Props) {
|
||||
if (!response || !response._shards || !Array.isArray(response._shards.failures) || !request) {
|
||||
// this should never ever happen, but just in case
|
||||
return (
|
||||
<EuiCallOut title="Sorry, there was an error" color="danger" iconType="alert">
|
||||
The ShardFailureModal component received invalid properties
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
||||
|
||||
const requestJSON = JSON.stringify(request, null, 2);
|
||||
const responseJSON = JSON.stringify(response, null, 2);
|
||||
const failures = response._shards.failures;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: 'table',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tabHeaderShardFailures', {
|
||||
defaultMessage: 'Shard failures',
|
||||
description: 'Name of the tab displaying shard failures',
|
||||
}),
|
||||
content: <ShardFailureTable failures={failures} />,
|
||||
},
|
||||
{
|
||||
id: 'json-request',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tabHeaderRequest', {
|
||||
defaultMessage: 'Request',
|
||||
description: 'Name of the tab displaying the JSON request',
|
||||
}),
|
||||
content: (
|
||||
<EuiCodeBlock language="json" isCopyable>
|
||||
{requestJSON}
|
||||
</EuiCodeBlock>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'json-response',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tabHeaderResponse', {
|
||||
defaultMessage: 'Response',
|
||||
description: 'Name of the tab displaying the JSON response',
|
||||
}),
|
||||
content: (
|
||||
<EuiCodeBlock language="json" isCopyable>
|
||||
{responseJSON}
|
||||
</EuiCodeBlock>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>{title}</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} autoFocus="selected" />
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiCopy textToCopy={responseJSON}>
|
||||
{copy => (
|
||||
<EuiButtonEmpty onClick={copy}>
|
||||
<FormattedMessage
|
||||
id="common.ui.courier.fetch.shardsFailedModal.copyToClipboard"
|
||||
defaultMessage="Copy response to clipboard"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</EuiCopy>
|
||||
<EuiButton onClick={() => onClose()} fill data-test-sub="closeShardFailureModal">
|
||||
<FormattedMessage
|
||||
id="common.ui.courier.fetch.shardsFailedModal.close"
|
||||
defaultMessage="Close"
|
||||
description="Closing the Modal"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export const openModal = jest.fn();
|
||||
|
||||
jest.doMock('ui/new_platform', () => {
|
||||
return {
|
||||
npStart: {
|
||||
core: {
|
||||
overlays: {
|
||||
openModal,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 { openModal } from './shard_failure_open_modal_button.test.mocks';
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { ShardFailureOpenModalButton } from './shard_failure_open_modal_button';
|
||||
import { shardFailureRequest } from './__mocks__/shard_failure_request';
|
||||
import { shardFailureResponse } from './__mocks__/shard_failure_response';
|
||||
// @ts-ignore
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
|
||||
describe('ShardFailureOpenModalButton', () => {
|
||||
it('triggers the openModal function when "Show details" button is clicked', () => {
|
||||
const component = mountWithIntl(
|
||||
<ShardFailureOpenModalButton
|
||||
request={shardFailureRequest}
|
||||
response={shardFailureResponse}
|
||||
title="test"
|
||||
/>
|
||||
);
|
||||
findTestSubject(component, 'openShardFailureModalBtn').simulate('click');
|
||||
expect(openModal).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
// @ts-ignore
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiButton, EuiTextAlign } from '@elastic/eui';
|
||||
|
||||
import { ShardFailureModal } from './shard_failure_modal';
|
||||
import { ResponseWithShardFailure, Request } from './shard_failure_types';
|
||||
|
||||
interface Props {
|
||||
request: Request;
|
||||
response: ResponseWithShardFailure;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function ShardFailureOpenModalButton({ request, response, title }: Props) {
|
||||
function onClick() {
|
||||
const modal = npStart.core.overlays.openModal(
|
||||
<ShardFailureModal
|
||||
request={request}
|
||||
response={response}
|
||||
title={title}
|
||||
onClose={() => modal.close()}
|
||||
/>,
|
||||
{
|
||||
className: 'shardFailureModal',
|
||||
}
|
||||
);
|
||||
}
|
||||
return (
|
||||
<EuiTextAlign textAlign="right">
|
||||
<EuiButton
|
||||
color="warning"
|
||||
size="s"
|
||||
onClick={onClick}
|
||||
data-test-subj="openShardFailureModalBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="common.ui.courier.fetch.shardsFailedModal.showDetails"
|
||||
defaultMessage="Show details"
|
||||
description="Open the modal to show details"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiTextAlign>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { ShardFailureTable } from './shard_failure_table';
|
||||
import { shardFailureResponse } from './__mocks__/shard_failure_response';
|
||||
import { ShardFailure } from './shard_failure_types';
|
||||
|
||||
describe('ShardFailureTable', () => {
|
||||
it('renders matching snapshot given valid properties', () => {
|
||||
const failures = shardFailureResponse._shards.failures as ShardFailure[];
|
||||
const component = shallowWithIntl(<ShardFailureTable failures={failures} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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 React, { useState, ReactElement } from 'react';
|
||||
// @ts-ignore
|
||||
import { EuiInMemoryTable, EuiButtonIcon } from '@elastic/eui';
|
||||
// @ts-ignore
|
||||
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ShardFailureDescription } from './shard_failure_description';
|
||||
import { ShardFailure } from './shard_failure_types';
|
||||
import { getFailureSummaryText } from './shard_failure_description_header';
|
||||
|
||||
export interface ListItem extends ShardFailure {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export function ShardFailureTable({ failures }: { failures: ShardFailure[] }) {
|
||||
const itemList = failures.map((failure, idx) => ({ ...{ id: String(idx) }, ...failure }));
|
||||
const initalMap = {} as Record<string, ReactElement>;
|
||||
|
||||
const [expandMap, setExpandMap] = useState(initalMap);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
align: RIGHT_ALIGNMENT,
|
||||
width: '40px',
|
||||
isExpander: true,
|
||||
render: (item: ListItem) => {
|
||||
const failureSummeryText = getFailureSummaryText(item);
|
||||
const collapseLabel = i18n.translate(
|
||||
'common.ui.courier.fetch.shardsFailedModal.tableRowCollapse',
|
||||
{
|
||||
defaultMessage: 'Collapse {rowDescription}',
|
||||
description: 'Collapse a row of a table with failures',
|
||||
values: { rowDescription: failureSummeryText },
|
||||
}
|
||||
);
|
||||
|
||||
const expandLabel = i18n.translate(
|
||||
'common.ui.courier.fetch.shardsFailedModal.tableRowExpand',
|
||||
{
|
||||
defaultMessage: 'Expand {rowDescription}',
|
||||
description: 'Expand a row of a table with failures',
|
||||
values: { rowDescription: failureSummeryText },
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
onClick={() => {
|
||||
// toggle displaying the expanded view of the given list item
|
||||
const map = Object.assign({}, expandMap);
|
||||
if (map[item.id]) {
|
||||
delete map[item.id];
|
||||
} else {
|
||||
map[item.id] = <ShardFailureDescription {...item} />;
|
||||
}
|
||||
setExpandMap(map);
|
||||
}}
|
||||
aria-label={expandMap[item.id] ? collapseLabel : expandLabel}
|
||||
iconType={expandMap[item.id] ? 'arrowUp' : 'arrowDown'}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'shard',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tableColShard', {
|
||||
defaultMessage: 'Shard',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
width: '80px',
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tableColIndex', {
|
||||
defaultMessage: 'Index',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'node',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tableColNode', {
|
||||
defaultMessage: 'Node',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'reason.type',
|
||||
name: i18n.translate('common.ui.courier.fetch.shardsFailedModal.tableColReason', {
|
||||
defaultMessage: 'Reason',
|
||||
}),
|
||||
truncateText: true,
|
||||
},
|
||||
];
|
||||
|
||||
const sorting = {
|
||||
sort: {
|
||||
field: 'index',
|
||||
direction: 'desc',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiInMemoryTable
|
||||
itemId="id"
|
||||
items={itemList}
|
||||
columns={columns}
|
||||
pagination={true}
|
||||
sorting={sorting}
|
||||
itemIdToExpandedRowMap={expandMap}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export interface Request {
|
||||
docvalue_fields: string[];
|
||||
_source: unknown;
|
||||
query: unknown;
|
||||
script_fields: unknown;
|
||||
sort: unknown;
|
||||
stored_fields: string[];
|
||||
}
|
||||
export interface ResponseWithShardFailure {
|
||||
_shards: {
|
||||
failed: number;
|
||||
failures: ShardFailure[];
|
||||
skipped: number;
|
||||
successful: number;
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ShardFailure {
|
||||
index: string;
|
||||
node: string;
|
||||
reason: {
|
||||
caused_by: {
|
||||
reason: string;
|
||||
type: string;
|
||||
};
|
||||
reason: string;
|
||||
lang?: string;
|
||||
script?: string;
|
||||
script_stack?: string[];
|
||||
type: string;
|
||||
};
|
||||
shard: number;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue