[Inspector Views] [Request View] - Migrate inspector_views to new platform (#43191)

* [Inspector Views] [Request View] - Migrate inspector_views to new platform

* fix i18n keys

* rename scss files

* fix PR comments
This commit is contained in:
Alexey Antonov 2019-08-19 16:54:28 +08:00 committed by GitHub
parent c19a2d5b57
commit 9f0de8dc6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 475 additions and 358 deletions

View file

@ -8,4 +8,6 @@
// insChart__legend-isLoading
@import 'data/index';
@import 'requests/index';
// Temporary reference
@import '../../../../plugins/inspector/public/views/index';

View file

@ -18,9 +18,6 @@
*/
import { DataView } from './data/data_view';
import { RequestsView } from './requests/requests_view';
import { viewRegistry } from 'ui/inspector';
viewRegistry.register(DataView);
viewRegistry.register(RequestsView);

View file

@ -1,125 +0,0 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiTab,
EuiTabs,
} from '@elastic/eui';
import {
RequestDetailsRequest,
RequestDetailsResponse,
RequestDetailsStats,
} from './details';
import { i18n } from '@kbn/i18n';
const DETAILS = [
{
name: 'Statistics',
label: i18n.translate('inspectorViews.requests.statisticsTabLabel', {
defaultMessage: 'Statistics'
}),
component: RequestDetailsStats
},
{
name: 'Request',
label: i18n.translate('inspectorViews.requests.requestTabLabel', {
defaultMessage: 'Request'
}),
component: RequestDetailsRequest
},
{
name: 'Response',
label: i18n.translate('inspectorViews.requests.responseTabLabel', {
defaultMessage: 'Response'
}),
component: RequestDetailsResponse
},
];
class RequestDetails extends Component {
state = {
availableDetails: [],
selectedDetail: null,
};
static getDerivedStateFromProps(nextProps, prevState) {
const selectedDetail = prevState && prevState.selectedDetail;
const availableDetails = DETAILS.filter(detail =>
!detail.component.shouldShow || detail.component.shouldShow(nextProps.request)
);
// If the previously selected detail is still available we want to stay
// on this tab and not set another selectedDetail.
if (selectedDetail && availableDetails.includes(selectedDetail)) {
return { availableDetails };
}
return {
availableDetails: availableDetails,
selectedDetail: availableDetails[0]
};
}
selectDetailsTab = (detail) => {
if (detail !== this.state.selectedDetail) {
this.setState({
selectedDetail: detail
});
}
};
renderDetailTab = (detail) => {
return (
<EuiTab
key={detail.name}
isSelected={detail === this.state.selectedDetail}
onClick={() => this.selectDetailsTab(detail)}
data-test-subj={`inspectorRequestDetail${detail.name}`}
>
{detail.label}
</EuiTab>
);
}
render() {
if (this.state.availableDetails.length === 0) {
return null;
}
const DetailComponent = this.state.selectedDetail.component;
return (
<div>
<EuiTabs size="s">
{ this.state.availableDetails.map(this.renderDetailTab) }
</EuiTabs>
<DetailComponent
request={this.props.request}
/>
</div>
);
}
}
RequestDetails.propTypes = {
request: PropTypes.object.isRequired,
};
export { RequestDetails };

View file

@ -52,11 +52,13 @@ export interface RequestParams {
}
export interface RequestStatistics {
[key: string]: {
label: string;
description?: string;
value: any;
};
[key: string]: RequestStatistic;
}
export interface RequestStatistic {
label: string;
description?: string;
value: any;
}
export interface Response {

View file

@ -24,6 +24,8 @@ import { InspectorViewRegistry } from './view_registry';
import { Adapters, InspectorOptions, InspectorSession } from './types';
import { InspectorPanel } from './ui/inspector_panel';
import { RequestsView } from './views';
export interface Setup {
registerView: InspectorViewRegistry['register'];
@ -66,6 +68,8 @@ export class InspectorPublicPlugin implements Plugin<Setup, Start> {
public async setup(core: CoreSetup) {
this.views = new InspectorViewRegistry();
this.views.register(RequestsView);
return {
registerView: this.views!.register.bind(this.views),

View file

@ -0,0 +1 @@
@import './requests/index';

View file

@ -17,6 +17,4 @@
* under the License.
*/
export * from './req_details_request';
export * from './req_details_response';
export * from './req_details_stats';
export { RequestsView } from './requests';

View file

@ -17,24 +17,6 @@
* under the License.
*/
import React from 'react';
import {
EuiCodeBlock,
} from '@elastic/eui';
function RequestDetailsResponse(props) {
return (
<EuiCodeBlock
language="json"
paddingSize="s"
isCopyable
data-test-subj="inspectorResponseBody"
>
{ JSON.stringify(props.request.response.json, null, 2) }
</EuiCodeBlock>
);
}
RequestDetailsResponse.shouldShow = (request) => request.response && request.response.json;
export { RequestDetailsResponse };
export { RequestDetailsRequest } from './req_details_request';
export { RequestDetailsResponse } from './req_details_response';
export { RequestDetailsStats } from './req_details_stats';

View file

@ -0,0 +1,51 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiCodeBlock } from '@elastic/eui';
import { Request } from '../../../../adapters/request/types';
import { RequestDetailsProps } from '../types';
export class RequestDetailsRequest extends Component<RequestDetailsProps> {
static propTypes = {
request: PropTypes.object.isRequired,
};
static shouldShow = (request: Request) => Boolean(request && request.json);
render() {
const { json } = this.props.request;
if (!json) {
return null;
}
return (
<EuiCodeBlock
language="json"
paddingSize="s"
isCopyable
data-test-subj="inspectorRequestBody"
>
{JSON.stringify(json, null, 2)}
</EuiCodeBlock>
);
}
}

View file

@ -0,0 +1,54 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiCodeBlock } from '@elastic/eui';
import { Request } from '../../../../adapters/request/types';
import { RequestDetailsProps } from '../types';
export class RequestDetailsResponse extends Component<RequestDetailsProps> {
static propTypes = {
request: PropTypes.object.isRequired,
};
static shouldShow = (request: Request) =>
Boolean(RequestDetailsResponse.getResponseJson(request));
static getResponseJson = (request: Request) => (request.response ? request.response.json : null);
render() {
const responseJSON = RequestDetailsResponse.getResponseJson(this.props.request);
if (!responseJSON) {
return null;
}
return (
<EuiCodeBlock
language="json"
paddingSize="s"
isCopyable
data-test-subj="inspectorResponseBody"
>
{JSON.stringify(responseJSON, null, 2)}
</EuiCodeBlock>
);
}
}

View file

@ -28,58 +28,62 @@ import {
EuiTableRowCell,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Request, RequestStatistic } from '../../../../adapters/request/types';
import { RequestDetailsProps } from '../types';
class RequestDetailsStats extends Component {
static shouldShow = (request) => !!request.stats && Object.keys(request.stats).length;
// TODO: Replace by property once available
interface RequestDetailsStatRow extends RequestStatistic {
id: string;
}
renderStatRow = (stat) => {
export class RequestDetailsStats extends Component<RequestDetailsProps> {
static propTypes = {
request: PropTypes.object.isRequired,
};
static shouldShow = (request: Request) =>
Boolean(request.stats && Object.keys(request.stats).length);
renderStatRow = (stat: RequestDetailsStatRow) => {
return [
<EuiTableRow
key={stat.id}
>
<EuiTableRow key={stat.id}>
<EuiTableRowCell>
<span className="insRequestDetailsStats__icon">
{ stat.description &&
{stat.description ? (
<EuiIconTip
aria-label={i18n.translate('inspectorViews.requests.descriptionRowIconAriaLabel', {
defaultMessage: 'Description'
aria-label={i18n.translate('inspector.requests.descriptionRowIconAriaLabel', {
defaultMessage: 'Description',
})}
type="questionInCircle"
color="subdued"
content={stat.description}
/>
}
{ !stat.description &&
<EuiIcon
type="empty"
/>
}
) : (
<EuiIcon type="empty" />
)}
</span>
{stat.label}
</EuiTableRowCell>
<EuiTableRowCell>{stat.value}</EuiTableRowCell>
</EuiTableRow>
</EuiTableRow>,
];
};
render() {
const { stats } = this.props.request;
const sortedStats = Object.keys(stats).sort().map(id => ({ id, ...stats[id] }));
// TODO: Replace by property once available
if (!stats) {
return null;
}
const sortedStats = Object.keys(stats)
.sort()
.map(id => ({ id, ...stats[id] } as RequestDetailsStatRow));
return (
<EuiTable
responsive={false}
>
<EuiTableBody>
{ sortedStats.map(this.renderStatRow) }
</EuiTableBody>
<EuiTable responsive={false}>
<EuiTableBody>{sortedStats.map(this.renderStatRow)}</EuiTableBody>
</EuiTable>
);
}
}
RequestDetailsStats.propTypes = {
request: PropTypes.object.isRequired,
};
export { RequestDetailsStats };

View file

@ -0,0 +1,131 @@
/*
* 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, { Component } from 'react';
import PropTypes from 'prop-types';
import { i18n } from '@kbn/i18n';
import { EuiTab, EuiTabs } from '@elastic/eui';
import { RequestDetailsRequest, RequestDetailsResponse, RequestDetailsStats } from './details';
import { RequestDetailsProps } from './types';
interface RequestDetailsState {
availableDetails: DetailViewData[];
selectedDetail: DetailViewData | null;
}
export interface DetailViewData {
name: string;
label: string;
component: any;
}
const DETAILS: DetailViewData[] = [
{
name: 'Statistics',
label: i18n.translate('inspector.requests.statisticsTabLabel', {
defaultMessage: 'Statistics',
}),
component: RequestDetailsStats,
},
{
name: 'Request',
label: i18n.translate('inspector.requests.requestTabLabel', {
defaultMessage: 'Request',
}),
component: RequestDetailsRequest,
},
{
name: 'Response',
label: i18n.translate('inspector.requests.responseTabLabel', {
defaultMessage: 'Response',
}),
component: RequestDetailsResponse,
},
];
export class RequestDetails extends Component<RequestDetailsProps, RequestDetailsState> {
static propTypes = {
request: PropTypes.object.isRequired,
};
state = {
availableDetails: [],
selectedDetail: null,
};
static getDerivedStateFromProps(nextProps: RequestDetailsProps, prevState: RequestDetailsState) {
const selectedDetail = prevState && prevState.selectedDetail;
const availableDetails = DETAILS.filter(
(detail: DetailViewData) =>
!detail.component.shouldShow || detail.component.shouldShow(nextProps.request)
);
// If the previously selected detail is still available we want to stay
// on this tab and not set another selectedDetail.
if (selectedDetail && availableDetails.includes(selectedDetail)) {
return { availableDetails };
}
return {
availableDetails,
selectedDetail: availableDetails[0],
};
}
selectDetailsTab = (detail: DetailViewData) => {
if (detail !== this.state.selectedDetail) {
this.setState({
selectedDetail: detail,
});
}
};
static getSelectedDetailComponent(detail: DetailViewData | null) {
return detail ? detail.component : null;
}
renderDetailTab = (detail: DetailViewData) => {
return (
<EuiTab
key={detail.name}
isSelected={detail === this.state.selectedDetail}
onClick={() => this.selectDetailsTab(detail)}
data-test-subj={`inspectorRequestDetail${detail.name}`}
>
{detail.label}
</EuiTab>
);
};
render() {
const { selectedDetail, availableDetails } = this.state;
const DetailComponent = RequestDetails.getSelectedDetailComponent(selectedDetail);
if (!availableDetails.length || !DetailComponent) {
return null;
}
return (
<>
<EuiTabs size="s">{this.state.availableDetails.map(this.renderDetailTab)}</EuiTabs>
<DetailComponent request={this.props.request} />
</>
);
}
}

View file

@ -18,7 +18,9 @@
*/
import React, { Component } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import PropTypes from 'prop-types';
import { i18n } from '@kbn/i18n';
import {
EuiBadge,
@ -33,29 +35,43 @@ import {
EuiToolTip,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { RequestStatus } from '../../../adapters';
import { Request } from '../../../adapters/request/types';
import { RequestStatus } from 'ui/inspector/adapters';
interface RequestSelectorState {
isPopoverOpen: boolean;
}
interface RequestSelectorProps {
requests: Request[];
selectedRequest: Request;
onRequestChanged: Function;
}
export class RequestSelector extends Component<RequestSelectorProps, RequestSelectorState> {
static propTypes = {
requests: PropTypes.array.isRequired,
selectedRequest: PropTypes.object.isRequired,
onRequestChanged: PropTypes.func,
};
class RequestSelector extends Component {
state = {
isPopoverOpen: false,
};
togglePopover = () => {
this.setState((prevState) => ({
this.setState((prevState: RequestSelectorState) => ({
isPopoverOpen: !prevState.isPopoverOpen,
}));
}
};
closePopover = () => {
this.setState({
isPopoverOpen: false,
});
}
};
renderRequestDropdownItem = (request, index) => {
renderRequestDropdownItem = (request: Request, index: number) => {
const hasFailed = request.status === RequestStatus.ERROR;
const inProgress = request.status === RequestStatus.PENDING;
@ -73,23 +89,24 @@ class RequestSelector extends Component {
>
<EuiTextColor color={hasFailed ? 'danger' : 'default'}>
{request.name}
{ hasFailed && <FormattedMessage
id="inspectorViews.requests.failedLabel"
defaultMessage=" (failed)"
/>}
{ inProgress &&
{hasFailed && (
<FormattedMessage id="inspector.requests.failedLabel" defaultMessage=" (failed)" />
)}
{inProgress && (
<EuiLoadingSpinner
size="s"
aria-label={i18n.translate('inspectorViews.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress'
aria-label={i18n.translate('inspector.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress',
})}
className="insRequestSelector__menuSpinner"
/>
}
)}
</EuiTextColor>
</EuiContextMenuItem>
);
}
};
renderRequestDropdown() {
const button = (
@ -124,78 +141,71 @@ class RequestSelector extends Component {
render() {
const { selectedRequest, requests } = this.props;
return (
<EuiFlexGroup
alignItems="center"
gutterSize="xs"
>
<EuiFlexItem
grow={false}
>
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<strong>
<FormattedMessage
id="inspectorViews.requests.requestLabel"
defaultMessage="Request:"
/>
<FormattedMessage id="inspector.requests.requestLabel" defaultMessage="Request:" />
</strong>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{requests.length <= 1 &&
<div className="insRequestSelector__singleRequest" data-test-subj="inspectorRequestName">
{requests.length <= 1 && (
<div
className="insRequestSelector__singleRequest"
data-test-subj="inspectorRequestName"
>
{selectedRequest.name}
</div>
}
)}
{requests.length > 1 && this.renderRequestDropdown()}
</EuiFlexItem>
<EuiFlexItem grow={false}>
{ selectedRequest.status !== RequestStatus.PENDING &&
{selectedRequest.status !== RequestStatus.PENDING && (
<EuiToolTip
position="left"
title={selectedRequest.status === RequestStatus.OK ?
title={
selectedRequest.status === RequestStatus.OK ? (
<FormattedMessage
id="inspector.requests.requestSucceededTooltipTitle"
defaultMessage="Request succeeded"
/>
) : (
<FormattedMessage
id="inspector.requests.requestFailedTooltipTitle"
defaultMessage="Request failed"
/>
)
}
content={
<FormattedMessage
id="inspectorViews.requests.requestSucceededTooltipTitle"
defaultMessage="Request succeeded"
/> :
<FormattedMessage
id="inspectorViews.requests.requestFailedTooltipTitle"
defaultMessage="Request failed"
id="inspector.requests.requestTooltipDescription"
defaultMessage="The total time the request took."
/>
}
content={<FormattedMessage
id="inspectorViews.requests.requestTooltipDescription"
defaultMessage="The total time the request took."
/>}
>
<EuiBadge
color={selectedRequest.status === RequestStatus.OK ? 'secondary' : 'danger'}
iconType={selectedRequest.status === RequestStatus.OK ? 'check' : 'cross'}
>
<FormattedMessage
id="inspectorViews.requests.requestTimeLabel"
id="inspector.requests.requestTimeLabel"
defaultMessage="{requestTime}ms"
values={{ requestTime: selectedRequest.time }}
/>
</EuiBadge>
</EuiToolTip>
}
{ selectedRequest.status === RequestStatus.PENDING &&
)}
{selectedRequest.status === RequestStatus.PENDING && (
<EuiLoadingSpinner
size="m"
aria-label={i18n.translate('inspectorViews.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress'
aria-label={i18n.translate('inspector.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress',
})}
/>
}
)}
</EuiFlexItem>
</EuiFlexGroup>
);
}
}
RequestSelector.propTypes = {
requests: PropTypes.array.isRequired,
selectedRequest: PropTypes.object.isRequired,
};
export { RequestSelector };

View file

@ -19,60 +19,67 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiEmptyPrompt,
EuiSpacer,
EuiText,
EuiTextColor,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiEmptyPrompt, EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui';
import { RequestStatus } from 'ui/inspector/adapters';
import { RequestStatus } from '../../../adapters';
import { Request } from '../../../adapters/request/types';
import { InspectorViewProps } from '../../../types';
import { RequestSelector } from './request_selector';
import { RequestDetails } from './request_details';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
class RequestsViewComponent extends Component {
interface RequestSelectorState {
requests: Request[];
request: Request;
}
constructor(props) {
export class RequestsViewComponent extends Component<InspectorViewProps, RequestSelectorState> {
static propTypes = {
adapters: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
};
constructor(props: InspectorViewProps) {
super(props);
props.adapters.requests.on('change', this._onRequestsChange);
const requests = props.adapters.requests.getRequests();
this.state = {
requests: requests,
request: requests.length ? requests[0] : null
requests,
request: requests.length ? requests[0] : null,
};
}
_onRequestsChange = () => {
const requests = this.props.adapters.requests.getRequests();
const newState = { requests };
const newState = { requests } as RequestSelectorState;
if (!requests.includes(this.state.request)) {
newState.request = requests.length ? requests[0] : null;
}
this.setState(newState);
}
};
selectRequest = (request) => {
selectRequest = (request: Request) => {
if (request !== this.state.request) {
this.setState({ request });
}
}
};
componentWillUnmount() {
this.props.adapters.requests.removeListener('change', this._onRequestsChange);
}
renderEmptyRequests() {
static renderEmptyRequests() {
return (
<EuiEmptyPrompt
data-test-subj="inspectorNoRequestsMessage"
title={
<h2>
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedTitle"
id="inspector.requests.noRequestsLoggedTitle"
defaultMessage="No requests logged"
/>
</h2>
@ -81,13 +88,13 @@ class RequestsViewComponent extends Component {
<React.Fragment>
<p>
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText"
id="inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText"
defaultMessage="The element hasn't logged any requests (yet)."
/>
</p>
<p>
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText"
id="inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText"
defaultMessage="This usually means that there was no need to fetch any data or
that the element has not yet started fetching data."
/>
@ -100,11 +107,11 @@ class RequestsViewComponent extends Component {
render() {
if (!this.state.requests || !this.state.requests.length) {
return this.renderEmptyRequests();
return RequestsViewComponent.renderEmptyRequests();
}
const failedCount = this.state.requests.filter(
req => req.status === RequestStatus.ERROR
(req: Request) => req.status === RequestStatus.ERROR
).length;
return (
@ -112,64 +119,44 @@ class RequestsViewComponent extends Component {
<EuiText size="xs">
<p role="status" aria-live="polite" aria-atomic="true">
<FormattedMessage
id="inspectorViews.requests.requestWasMadeDescription"
id="inspector.requests.requestWasMadeDescription"
defaultMessage="{requestsCount, plural, one {# request was} other {# requests were} } made{failedRequests}"
values={{
requestsCount: this.state.requests.length,
failedRequests: (
failedRequests:
failedCount > 0 ? (
<EuiTextColor color="danger">
<FormattedMessage
id="inspectorViews.requests.requestWasMadeDescription.requestHadFailureText"
id="inspector.requests.requestWasMadeDescription.requestHadFailureText"
defaultMessage=", {failedCount} had a failure"
values={{ failedCount }}
/>
</EuiTextColor>
) : ''
)
) : (
''
),
}}
/>
</p>
</EuiText>
<EuiSpacer size="xs"/>
<EuiSpacer size="xs" />
<RequestSelector
requests={this.state.requests}
selectedRequest={this.state.request}
onRequestChanged={this.selectRequest}
/>
<EuiSpacer size="xs"/>
{ this.state.request && this.state.request.description &&
<EuiSpacer size="xs" />
{this.state.request && this.state.request.description && (
<EuiText size="xs">
<p>{this.state.request.description}</p>
</EuiText>
}
)}
<EuiSpacer size="m" />
{ this.state.request &&
<RequestDetails
request={this.state.request}
/>
}
{this.state.request && <RequestDetails request={this.state.request} />}
</>
);
}
}
RequestsViewComponent.propTypes = {
adapters: PropTypes.object.isRequired,
};
const RequestsView = {
title: i18n.translate('inspectorViews.requests.requestsTitle', {
defaultMessage: 'Requests'
}),
order: 20,
help: i18n.translate('inspectorViews.requests.requestsDescriptionTooltip', {
defaultMessage: 'View the requests that collected the data'
}),
shouldShow(adapters) {
return Boolean(adapters.requests);
},
component: RequestsViewComponent
};
export { RequestsView };

View file

@ -16,25 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Request } from '../../../adapters/request/types';
import React from 'react';
import {
EuiCodeBlock,
} from '@elastic/eui';
function RequestDetailsRequest(props) {
return (
<EuiCodeBlock
language="json"
paddingSize="s"
isCopyable
data-test-subj="inspectorRequestBody"
>
{ JSON.stringify(props.request.json, null, 2) }
</EuiCodeBlock>
);
export interface RequestDetailsProps {
request: Request;
}
RequestDetailsRequest.shouldShow = (request) => !!request.json;
export { RequestDetailsRequest };

View file

@ -0,0 +1,36 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { RequestsViewComponent } from './components/requests_view';
import { Adapters, InspectorViewDescription } from '../../types';
export const RequestsView: InspectorViewDescription = {
title: i18n.translate('inspector.requests.requestsTitle', {
defaultMessage: 'Requests',
}),
order: 20,
help: i18n.translate('inspector.requests.requestsDescriptionTooltip', {
defaultMessage: 'View the requests that collected the data',
}),
shouldShow(adapters: Adapters) {
return Boolean(adapters.requests);
},
component: RequestsViewComponent,
};

View file

@ -887,24 +887,24 @@
"inspectorViews.data.noDataAvailableTitle": "利用可能なデータがありません",
"inspectorViews.data.rawCSVButtonLabel": "CSV",
"inspectorViews.data.rawCSVButtonTooltip": "日付をタイムスタンプとしてなど、提供されたデータをそのままダウンロードします",
"inspectorViews.requests.descriptionRowIconAriaLabel": "説明",
"inspectorViews.requests.failedLabel": " (失敗)",
"inspectorViews.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "エレメントが (まだ) リクエストを記録していません。",
"inspectorViews.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "これは通常、データを取得する必要がないか、エレメントがまだデータの取得を開始していないことを意味します。",
"inspectorViews.requests.noRequestsLoggedTitle": "リクエストが記録されていません",
"inspectorViews.requests.requestFailedTooltipTitle": "リクエストに失敗しました",
"inspectorViews.requests.requestInProgressAriaLabel": "リクエスト進行中",
"inspectorViews.requests.requestLabel": "リクエスト",
"inspectorViews.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します",
"inspectorViews.requests.requestsTitle": "リクエスト",
"inspectorViews.requests.requestSucceededTooltipTitle": "リクエスト成功",
"inspectorViews.requests.requestTabLabel": "リクエスト",
"inspectorViews.requests.requestTimeLabel": "{requestTime}ms",
"inspectorViews.requests.requestTooltipDescription": "リクエストの合計所要時間です。",
"inspectorViews.requests.requestWasMadeDescription": "{requestsCount, plural, one {# リクエストが} other {# リクエストが} } 行われました{failedRequests}",
"inspectorViews.requests.requestWasMadeDescription.requestHadFailureText": "、{failedCount} 件に失敗がありました",
"inspectorViews.requests.responseTabLabel": "応答",
"inspectorViews.requests.statisticsTabLabel": "統計",
"inspector.requests.descriptionRowIconAriaLabel": "説明",
"inspector.requests.failedLabel": " (失敗)",
"inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "エレメントが (まだ) リクエストを記録していません。",
"inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "これは通常、データを取得する必要がないか、エレメントがまだデータの取得を開始していないことを意味します。",
"inspector.requests.noRequestsLoggedTitle": "リクエストが記録されていません",
"inspector.requests.requestFailedTooltipTitle": "リクエストに失敗しました",
"inspector.requests.requestInProgressAriaLabel": "リクエスト進行中",
"inspector.requests.requestLabel": "リクエスト",
"inspector.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します",
"inspector.requests.requestsTitle": "リクエスト",
"inspector.requests.requestSucceededTooltipTitle": "リクエスト成功",
"inspector.requests.requestTabLabel": "リクエスト",
"inspector.requests.requestTimeLabel": "{requestTime}ms",
"inspector.requests.requestTooltipDescription": "リクエストの合計所要時間です。",
"inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# リクエストが} other {# リクエストが} } 行われました{failedRequests}",
"inspector.requests.requestWasMadeDescription.requestHadFailureText": "、{failedCount} 件に失敗がありました",
"inspector.requests.responseTabLabel": "応答",
"inspector.requests.statisticsTabLabel": "統計",
"interpreter.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名)",
"interpreter.function.visDimension.error.accessor": "入力された列名は無効です。",
"interpreter.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",

View file

@ -887,24 +887,24 @@
"inspectorViews.data.noDataAvailableTitle": "没有可用数据",
"inspectorViews.data.rawCSVButtonLabel": "原始 CSV",
"inspectorViews.data.rawCSVButtonTooltip": "按原样下载数据,例如将日期作为时间戳下载",
"inspectorViews.requests.descriptionRowIconAriaLabel": "描述",
"inspectorViews.requests.failedLabel": " (失败)",
"inspectorViews.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "该元素尚未记录任何请求。",
"inspectorViews.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "这通常表示无需提取任何数据,或该元素尚未开始提取数据。",
"inspectorViews.requests.noRequestsLoggedTitle": "未记录任何请求",
"inspectorViews.requests.requestFailedTooltipTitle": "请求失败",
"inspectorViews.requests.requestInProgressAriaLabel": "请求进行中",
"inspectorViews.requests.requestLabel": "请求:",
"inspectorViews.requests.requestsDescriptionTooltip": "查看已收集数据的请求",
"inspectorViews.requests.requestsTitle": "请求",
"inspectorViews.requests.requestSucceededTooltipTitle": "请求成功",
"inspectorViews.requests.requestTabLabel": "请求",
"inspectorViews.requests.requestTimeLabel": "{requestTime}ms",
"inspectorViews.requests.requestTooltipDescription": "请求所花费的总时间。",
"inspectorViews.requests.requestWasMadeDescription": "{requestsCount, plural, one {# 个请求已} other {# 个请求已} }发出{failedRequests}",
"inspectorViews.requests.requestWasMadeDescription.requestHadFailureText": "{failedCount} 个失败",
"inspectorViews.requests.responseTabLabel": "响应",
"inspectorViews.requests.statisticsTabLabel": "统计信息",
"inspector.requests.descriptionRowIconAriaLabel": "描述",
"inspector.requests.failedLabel": " (失败)",
"inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "该元素尚未记录任何请求。",
"inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "这通常表示无需提取任何数据,或该元素尚未开始提取数据。",
"inspector.requests.noRequestsLoggedTitle": "未记录任何请求",
"inspector.requests.requestFailedTooltipTitle": "请求失败",
"inspector.requests.requestInProgressAriaLabel": "请求进行中",
"inspector.requests.requestLabel": "请求:",
"inspector.requests.requestsDescriptionTooltip": "查看已收集数据的请求",
"inspector.requests.requestsTitle": "请求",
"inspector.requests.requestSucceededTooltipTitle": "请求成功",
"inspector.requests.requestTabLabel": "请求",
"inspector.requests.requestTimeLabel": "{requestTime}ms",
"inspector.requests.requestTooltipDescription": "请求所花费的总时间。",
"inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# 个请求已} other {# 个请求已} }发出{failedRequests}",
"inspector.requests.requestWasMadeDescription.requestHadFailureText": "{failedCount} 个失败",
"inspector.requests.responseTabLabel": "响应",
"inspector.requests.statisticsTabLabel": "统计信息",
"interpreter.function.visDimension.accessor.help": "数据集中要使用的列(列索引或列名称)",
"interpreter.function.visDimension.error.accessor": "提供的列名称无效",
"interpreter.function.visDimension.help": "生成 visConfig 维度对象",