mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[APM] Convert most of remaining js to ts (#32115)
* [APM] Convert most of remaining js to ts * Fixes for distribution component * Make `page` and `sort` optional * Add Server definition from hapi
This commit is contained in:
parent
95a3284637
commit
aa71146252
36 changed files with 291 additions and 232 deletions
|
@ -4,18 +4,20 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { Server } from 'hapi';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { initTransactionGroupsApi } from './server/routes/transaction_groups';
|
import mappings from './mappings.json';
|
||||||
import { initServicesApi } from './server/routes/services';
|
import { makeApmUsageCollector } from './server/lib/apm_telemetry';
|
||||||
import { initErrorsApi } from './server/routes/errors';
|
import { initErrorsApi } from './server/routes/errors';
|
||||||
|
import { initMetricsApi } from './server/routes/metrics';
|
||||||
|
import { initServicesApi } from './server/routes/services';
|
||||||
import { initStatusApi } from './server/routes/status_check';
|
import { initStatusApi } from './server/routes/status_check';
|
||||||
import { initTracesApi } from './server/routes/traces';
|
import { initTracesApi } from './server/routes/traces';
|
||||||
import { initMetricsApi } from './server/routes/metrics';
|
import { initTransactionGroupsApi } from './server/routes/transaction_groups';
|
||||||
import mappings from './mappings';
|
|
||||||
import { makeApmUsageCollector } from './server/lib/apm_telemetry';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
|
|
||||||
export function apm(kibana) {
|
// TODO: get proper types
|
||||||
|
export function apm(kibana: any) {
|
||||||
return new kibana.Plugin({
|
return new kibana.Plugin({
|
||||||
require: ['kibana', 'elasticsearch', 'xpack_main', 'apm_oss'],
|
require: ['kibana', 'elasticsearch', 'xpack_main', 'apm_oss'],
|
||||||
id: 'apm',
|
id: 'apm',
|
||||||
|
@ -35,7 +37,9 @@ export function apm(kibana) {
|
||||||
},
|
},
|
||||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||||
home: ['plugins/apm/register_feature'],
|
home: ['plugins/apm/register_feature'],
|
||||||
injectDefaultVars(server) {
|
|
||||||
|
// TODO: get proper types
|
||||||
|
injectDefaultVars(server: Server) {
|
||||||
const config = server.config();
|
const config = server.config();
|
||||||
return {
|
return {
|
||||||
apmUiEnabled: config.get('xpack.apm.ui.enabled'),
|
apmUiEnabled: config.get('xpack.apm.ui.enabled'),
|
||||||
|
@ -51,7 +55,8 @@ export function apm(kibana) {
|
||||||
mappings
|
mappings
|
||||||
},
|
},
|
||||||
|
|
||||||
config(Joi) {
|
// TODO: get proper types
|
||||||
|
config(Joi: any) {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
// display menu item
|
// display menu item
|
||||||
ui: Joi.object({
|
ui: Joi.object({
|
||||||
|
@ -68,7 +73,8 @@ export function apm(kibana) {
|
||||||
}).default();
|
}).default();
|
||||||
},
|
},
|
||||||
|
|
||||||
init(server) {
|
// TODO: get proper types
|
||||||
|
init(server: any) {
|
||||||
initTransactionGroupsApi(server);
|
initTransactionGroupsApi(server);
|
||||||
initTracesApi(server);
|
initTracesApi(server);
|
||||||
initServicesApi(server);
|
initServicesApi(server);
|
|
@ -7,10 +7,32 @@
|
||||||
import { EuiTitle } from '@elastic/eui';
|
import { EuiTitle } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
// @ts-ignore
|
||||||
import Histogram from '../../../shared/charts/Histogram';
|
import Histogram from '../../../shared/charts/Histogram';
|
||||||
import { EmptyMessage } from '../../../shared/EmptyMessage';
|
import { EmptyMessage } from '../../../shared/EmptyMessage';
|
||||||
|
|
||||||
export function getFormattedBuckets(buckets, bucketSize) {
|
interface IBucket {
|
||||||
|
key: number;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cleanup duplication of this in distribution/get_distribution.ts (ErrorDistributionAPIResponse) and transactions/distribution/index.ts (ITransactionDistributionAPIResponse)
|
||||||
|
interface IDistribution {
|
||||||
|
totalHits: number;
|
||||||
|
buckets: IBucket[];
|
||||||
|
bucketSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormattedBucket {
|
||||||
|
x0: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFormattedBuckets(
|
||||||
|
buckets: IBucket[],
|
||||||
|
bucketSize: number
|
||||||
|
): FormattedBucket[] | null {
|
||||||
if (!buckets) {
|
if (!buckets) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -24,12 +46,12 @@ export function getFormattedBuckets(buckets, bucketSize) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function Distribution({
|
interface Props {
|
||||||
distribution,
|
distribution: IDistribution;
|
||||||
title = i18n.translate('xpack.apm.errorGroupDetails.occurrencesChartLabel', {
|
title: React.ReactNode;
|
||||||
defaultMessage: 'Occurrences'
|
}
|
||||||
})
|
|
||||||
}) {
|
export function ErrorDistribution({ distribution, title }: Props) {
|
||||||
const buckets = getFormattedBuckets(
|
const buckets = getFormattedBuckets(
|
||||||
distribution.buckets,
|
distribution.buckets,
|
||||||
distribution.bucketSize
|
distribution.bucketSize
|
||||||
|
@ -53,17 +75,17 @@ function Distribution({
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
</EuiTitle>
|
</EuiTitle>
|
||||||
<Histogram
|
<Histogram
|
||||||
verticalLineHover={bucket => bucket.x}
|
verticalLineHover={(bucket: FormattedBucket) => bucket.x}
|
||||||
xType="time"
|
xType="time"
|
||||||
buckets={buckets}
|
buckets={buckets}
|
||||||
bucketSize={distribution.bucketSize}
|
bucketSize={distribution.bucketSize}
|
||||||
formatYShort={value =>
|
formatYShort={(value: number) =>
|
||||||
i18n.translate('xpack.apm.errorGroupDetails.occurrencesShortLabel', {
|
i18n.translate('xpack.apm.errorGroupDetails.occurrencesShortLabel', {
|
||||||
defaultMessage: '{occCount} occ.',
|
defaultMessage: '{occCount} occ.',
|
||||||
values: { occCount: value }
|
values: { occCount: value }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
formatYLong={value =>
|
formatYLong={(value: number) =>
|
||||||
i18n.translate('xpack.apm.errorGroupDetails.occurrencesLongLabel', {
|
i18n.translate('xpack.apm.errorGroupDetails.occurrencesLongLabel', {
|
||||||
defaultMessage: '{occCount} occurrences',
|
defaultMessage: '{occCount} occurrences',
|
||||||
values: { occCount: value }
|
values: { occCount: value }
|
||||||
|
@ -73,5 +95,3 @@ function Distribution({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Distribution;
|
|
|
@ -5,10 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { ErrorGroupDetails } from './view';
|
import { IReduxState } from '../../../store/rootReducer';
|
||||||
import { getUrlParams } from '../../../store/urlParams';
|
import { getUrlParams } from '../../../store/urlParams';
|
||||||
|
import { ErrorGroupDetailsView } from './view';
|
||||||
|
|
||||||
function mapStateToProps(state = {}) {
|
function mapStateToProps(state = {} as IReduxState) {
|
||||||
return {
|
return {
|
||||||
urlParams: getUrlParams(state),
|
urlParams: getUrlParams(state),
|
||||||
location: state.location
|
location: state.location
|
||||||
|
@ -17,7 +18,7 @@ function mapStateToProps(state = {}) {
|
||||||
|
|
||||||
const mapDispatchToProps = {};
|
const mapDispatchToProps = {};
|
||||||
|
|
||||||
export default connect(
|
export const ErrorGroupDetails = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ErrorGroupDetails);
|
)(ErrorGroupDetailsView);
|
|
@ -25,8 +25,7 @@ import {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { KueryBar } from '../../shared/KueryBar';
|
import { KueryBar } from '../../shared/KueryBar';
|
||||||
import { DetailView } from './DetailView';
|
import { DetailView } from './DetailView';
|
||||||
// @ts-ignore
|
import { ErrorDistribution } from './Distribution';
|
||||||
import Distribution from './Distribution';
|
|
||||||
|
|
||||||
const Titles = styled.div`
|
const Titles = styled.div`
|
||||||
margin-bottom: ${px(units.plus)};
|
margin-bottom: ${px(units.plus)};
|
||||||
|
@ -67,7 +66,7 @@ interface Props {
|
||||||
location: Location;
|
location: Location;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ErrorGroupDetails({ urlParams, location }: Props) {
|
export function ErrorGroupDetailsView({ urlParams, location }: Props) {
|
||||||
return (
|
return (
|
||||||
<ErrorGroupDetailsRequest
|
<ErrorGroupDetailsRequest
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
|
@ -152,7 +151,17 @@ export function ErrorGroupDetails({ urlParams, location }: Props) {
|
||||||
)}
|
)}
|
||||||
<ErrorDistributionRequest
|
<ErrorDistributionRequest
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
render={({ data }) => <Distribution distribution={data} />}
|
render={({ data }) => (
|
||||||
|
<ErrorDistribution
|
||||||
|
distribution={data}
|
||||||
|
title={i18n.translate(
|
||||||
|
'xpack.apm.errorGroupDetails.occurrencesChartLabel',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Occurrences'
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
<DetailView
|
<DetailView
|
||||||
|
|
|
@ -5,15 +5,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
|
import { Location } from 'history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
mockMoment,
|
mockMoment,
|
||||||
mountWithRouterAndStore,
|
mountWithRouterAndStore,
|
||||||
toJson
|
toJson
|
||||||
// @ts-ignore
|
|
||||||
} from '../../../../../utils/testHelpers';
|
} from '../../../../../utils/testHelpers';
|
||||||
// @ts-ignore
|
|
||||||
import { ErrorGroupList } from '../index';
|
import { ErrorGroupList } from '../index';
|
||||||
import props from './props.json';
|
import props from './props.json';
|
||||||
|
|
||||||
|
@ -26,7 +25,11 @@ describe('ErrorGroupOverview -> List', () => {
|
||||||
const storeState = {};
|
const storeState = {};
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<ErrorGroupList items={[]} urlParams={props.urlParams} location={{}} />
|
<ErrorGroupList
|
||||||
|
items={[]}
|
||||||
|
urlParams={props.urlParams}
|
||||||
|
location={{} as Location}
|
||||||
|
/>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
storeState
|
storeState
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,25 +4,35 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import { EuiBadge, EuiBasicTable, EuiToolTip } from '@elastic/eui';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { EuiBasicTable, EuiBadge, EuiToolTip } from '@elastic/eui';
|
|
||||||
import numeral from '@elastic/numeral';
|
import numeral from '@elastic/numeral';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { Location } from 'history';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { toQuery, fromQuery, history } from '../../../shared/Links/url_helpers';
|
import React, { Component } from 'react';
|
||||||
import { KibanaLink } from '../../../shared/Links/KibanaLink';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { IUrlParams } from 'x-pack/plugins/apm/public/store/urlParams';
|
||||||
|
import { ErrorGroupListAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_error_groups';
|
||||||
|
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
||||||
import {
|
import {
|
||||||
unit,
|
|
||||||
px,
|
|
||||||
fontFamilyCode,
|
fontFamilyCode,
|
||||||
fontSizes,
|
fontSizes,
|
||||||
truncate
|
px,
|
||||||
|
truncate,
|
||||||
|
unit
|
||||||
} from '../../../../style/variables';
|
} from '../../../../style/variables';
|
||||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
import { KibanaLink } from '../../../shared/Links/KibanaLink';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { fromQuery, history, toQuery } from '../../../shared/Links/url_helpers';
|
||||||
|
|
||||||
function paginateItems({ items, pageIndex, pageSize }) {
|
function paginateItems({
|
||||||
|
items,
|
||||||
|
pageIndex,
|
||||||
|
pageSize
|
||||||
|
}: {
|
||||||
|
items: any[];
|
||||||
|
pageIndex: number;
|
||||||
|
pageSize: number;
|
||||||
|
}) {
|
||||||
return items.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
return items.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,15 +54,33 @@ const Culprit = styled.div`
|
||||||
font-family: ${fontFamilyCode};
|
font-family: ${fontFamilyCode};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export class ErrorGroupList extends Component {
|
interface Props {
|
||||||
state = {
|
location: Location;
|
||||||
|
urlParams: IUrlParams;
|
||||||
|
items: ErrorGroupListAPIResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITableChange {
|
||||||
|
page: { index?: number; size?: number };
|
||||||
|
sort: {
|
||||||
|
field?: string;
|
||||||
|
direction?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
page: { index?: number; size?: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorGroupList extends Component<Props, State> {
|
||||||
|
public state = {
|
||||||
page: {
|
page: {
|
||||||
index: 0,
|
index: 0,
|
||||||
size: 25
|
size: 25
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onTableChange = ({ page = {}, sort = {} }) => {
|
public onTableChange = ({ page = {}, sort = {} }: ITableChange) => {
|
||||||
this.setState({ page });
|
this.setState({ page });
|
||||||
|
|
||||||
const { location } = this.props;
|
const { location } = this.props;
|
||||||
|
@ -67,7 +95,7 @@ export class ErrorGroupList extends Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
public render() {
|
||||||
const { items } = this.props;
|
const { items } = this.props;
|
||||||
const { serviceName, sortDirection, sortField } = this.props.urlParams;
|
const { serviceName, sortDirection, sortField } = this.props.urlParams;
|
||||||
|
|
||||||
|
@ -85,7 +113,7 @@ export class ErrorGroupList extends Component {
|
||||||
field: 'groupId',
|
field: 'groupId',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
width: px(unit * 6),
|
width: px(unit * 6),
|
||||||
render: groupId => {
|
render: (groupId: string) => {
|
||||||
return (
|
return (
|
||||||
<GroupIdLink hash={`/${serviceName}/errors/${groupId}`}>
|
<GroupIdLink hash={`/${serviceName}/errors/${groupId}`}>
|
||||||
{groupId.slice(0, 5) || NOT_AVAILABLE_LABEL}
|
{groupId.slice(0, 5) || NOT_AVAILABLE_LABEL}
|
||||||
|
@ -103,7 +131,7 @@ export class ErrorGroupList extends Component {
|
||||||
field: 'message',
|
field: 'message',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
width: '50%',
|
width: '50%',
|
||||||
render: (message, item) => {
|
render: (message: string, item: ErrorGroupListAPIResponse[0]) => {
|
||||||
return (
|
return (
|
||||||
<MessageAndCulpritCell>
|
<MessageAndCulpritCell>
|
||||||
<EuiToolTip
|
<EuiToolTip
|
||||||
|
@ -130,7 +158,7 @@ export class ErrorGroupList extends Component {
|
||||||
field: 'handled',
|
field: 'handled',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
render: isUnhandled =>
|
render: (isUnhandled: boolean) =>
|
||||||
isUnhandled === false && (
|
isUnhandled === false && (
|
||||||
<EuiBadge color="warning">
|
<EuiBadge color="warning">
|
||||||
{i18n.translate('xpack.apm.errorsTable.unhandledLabel', {
|
{i18n.translate('xpack.apm.errorsTable.unhandledLabel', {
|
||||||
|
@ -146,7 +174,7 @@ export class ErrorGroupList extends Component {
|
||||||
field: 'occurrenceCount',
|
field: 'occurrenceCount',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
dataType: 'number',
|
dataType: 'number',
|
||||||
render: value =>
|
render: (value?: number) =>
|
||||||
value ? numeral(value).format('0.[0]a') : NOT_AVAILABLE_LABEL
|
value ? numeral(value).format('0.[0]a') : NOT_AVAILABLE_LABEL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -159,7 +187,8 @@ export class ErrorGroupList extends Component {
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
align: 'right',
|
align: 'right',
|
||||||
render: value => (value ? moment(value).fromNow() : NOT_AVAILABLE_LABEL)
|
render: (value?: number) =>
|
||||||
|
value ? moment(value).fromNow() : NOT_AVAILABLE_LABEL
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -186,7 +215,3 @@ export class ErrorGroupList extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorGroupList.propTypes = {
|
|
||||||
location: PropTypes.object.isRequired
|
|
||||||
};
|
|
|
@ -4,16 +4,14 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { Location } from 'history';
|
import { Location } from 'history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
// @ts-ignore
|
import { ErrorDistribution } from 'x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution';
|
||||||
import Distribution from 'x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution';
|
|
||||||
import { ErrorDistributionRequest } from 'x-pack/plugins/apm/public/store/reactReduxRequest/errorDistribution';
|
import { ErrorDistributionRequest } from 'x-pack/plugins/apm/public/store/reactReduxRequest/errorDistribution';
|
||||||
import { IUrlParams } from 'x-pack/plugins/apm/public/store/urlParams';
|
import { IUrlParams } from 'x-pack/plugins/apm/public/store/urlParams';
|
||||||
import { ErrorGroupOverviewRequest } from '../../../store/reactReduxRequest/errorGroupList';
|
import { ErrorGroupOverviewRequest } from '../../../store/reactReduxRequest/errorGroupList';
|
||||||
// @ts-ignore
|
|
||||||
import { ErrorGroupList } from './List';
|
import { ErrorGroupList } from './List';
|
||||||
|
|
||||||
interface ErrorGroupOverviewProps {
|
interface ErrorGroupOverviewProps {
|
||||||
|
@ -32,20 +30,14 @@ const ErrorGroupOverview: React.SFC<ErrorGroupOverviewProps> = ({
|
||||||
<ErrorDistributionRequest
|
<ErrorDistributionRequest
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
render={({ data }) => (
|
render={({ data }) => (
|
||||||
<Distribution
|
<ErrorDistribution
|
||||||
distribution={data}
|
distribution={data}
|
||||||
title={
|
title={i18n.translate(
|
||||||
<EuiTitle size="xs">
|
'xpack.apm.serviceDetails.metrics.errorOccurrencesChartTitle',
|
||||||
<span>
|
{
|
||||||
{i18n.translate(
|
defaultMessage: 'Error occurrences'
|
||||||
'xpack.apm.serviceDetails.metrics.errorOccurrencesChartTitle',
|
}
|
||||||
{
|
)}
|
||||||
defaultMessage: 'Error occurrences'
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</EuiTitle>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,22 +4,23 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { get, some } from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import view from './view';
|
import { IReduxState } from 'x-pack/plugins/apm/public/store/rootReducer';
|
||||||
import { some, get } from 'lodash';
|
|
||||||
import { STATUS } from '../../../../constants/index';
|
import { STATUS } from '../../../../constants/index';
|
||||||
|
import { GlobalProgressView } from './view';
|
||||||
|
|
||||||
function getIsLoading(state) {
|
function getIsLoading(state: IReduxState) {
|
||||||
return some(
|
return some(
|
||||||
state.reactReduxRequest,
|
state.reactReduxRequest,
|
||||||
subState => get(subState, 'status') === STATUS.LOADING
|
subState => get(subState, 'status') === STATUS.LOADING
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps(state = {}) {
|
function mapStateToProps(state = {} as IReduxState) {
|
||||||
return {
|
return {
|
||||||
isLoading: getIsLoading(state)
|
isLoading: getIsLoading(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps)(view);
|
export const GlobalProgress = connect(mapStateToProps)(GlobalProgressView);
|
|
@ -4,10 +4,14 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { EuiDelayHide, EuiPortal, EuiProgress } from '@elastic/eui';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { EuiPortal, EuiProgress, EuiDelayHide } from '@elastic/eui';
|
|
||||||
|
|
||||||
export default ({ isLoading }) => {
|
interface Props {
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GlobalProgressView({ isLoading }: Props) {
|
||||||
return (
|
return (
|
||||||
<EuiDelayHide
|
<EuiDelayHide
|
||||||
hide={!isLoading}
|
hide={!isLoading}
|
||||||
|
@ -19,4 +23,4 @@ export default ({ isLoading }) => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
}
|
|
@ -4,18 +4,21 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Location } from 'history';
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
|
|
||||||
class ScrollToTopOnPathChange extends Component {
|
interface Props {
|
||||||
componentDidUpdate(prevProps) {
|
location: Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScrollToTopOnPathChange extends Component<Props> {
|
||||||
|
public componentDidUpdate(prevProps: Props) {
|
||||||
if (this.props.location.pathname !== prevProps.location.pathname) {
|
if (this.props.location.pathname !== prevProps.location.pathname) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ScrollToTopOnPathChange;
|
|
|
@ -12,8 +12,7 @@ import { px, topNavHeight, unit, units } from '../../../style/variables';
|
||||||
import ConnectRouterToRedux from '../../shared/ConnectRouterToRedux';
|
import ConnectRouterToRedux from '../../shared/ConnectRouterToRedux';
|
||||||
import { LicenseCheck } from './LicenseCheck';
|
import { LicenseCheck } from './LicenseCheck';
|
||||||
import { routes } from './routeConfig';
|
import { routes } from './routeConfig';
|
||||||
// @ts-ignore
|
import { ScrollToTopOnPathChange } from './ScrollToTopOnPathChange';
|
||||||
import ScrollToTopOnPathChange from './ScrollToTopOnPathChange';
|
|
||||||
import { UpdateBreadcrumbs } from './UpdateBreadcrumbs';
|
import { UpdateBreadcrumbs } from './UpdateBreadcrumbs';
|
||||||
|
|
||||||
const MainContainer = styled.div`
|
const MainContainer = styled.div`
|
||||||
|
|
|
@ -8,8 +8,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Redirect, RouteComponentProps } from 'react-router-dom';
|
import { Redirect, RouteComponentProps } from 'react-router-dom';
|
||||||
import { legacyDecodeURIComponent } from 'x-pack/plugins/apm/public/components/shared/Links/url_helpers';
|
import { legacyDecodeURIComponent } from 'x-pack/plugins/apm/public/components/shared/Links/url_helpers';
|
||||||
// @ts-ignore
|
import { ErrorGroupDetails } from '../ErrorGroupDetails';
|
||||||
import ErrorGroupDetails from '../ErrorGroupDetails';
|
|
||||||
import { ServiceDetails } from '../ServiceDetails';
|
import { ServiceDetails } from '../ServiceDetails';
|
||||||
import { TransactionDetails } from '../TransactionDetails';
|
import { TransactionDetails } from '../TransactionDetails';
|
||||||
import { Home } from './Home';
|
import { Home } from './Home';
|
||||||
|
|
|
@ -8,14 +8,12 @@ import {
|
||||||
EuiFlexGrid,
|
EuiFlexGrid,
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiSpacer,
|
EuiSpacer
|
||||||
EuiTitle
|
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { Location } from 'history';
|
import { Location } from 'history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
// @ts-ignore
|
import { ErrorDistribution } from 'x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution';
|
||||||
import Distribution from 'x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution';
|
|
||||||
import { SyncChartGroup } from 'x-pack/plugins/apm/public/components/shared/charts/SyncChartGroup';
|
import { SyncChartGroup } from 'x-pack/plugins/apm/public/components/shared/charts/SyncChartGroup';
|
||||||
import { TransactionCharts } from 'x-pack/plugins/apm/public/components/shared/charts/TransactionCharts';
|
import { TransactionCharts } from 'x-pack/plugins/apm/public/components/shared/charts/TransactionCharts';
|
||||||
import { ErrorDistributionRequest } from 'x-pack/plugins/apm/public/store/reactReduxRequest/errorDistribution';
|
import { ErrorDistributionRequest } from 'x-pack/plugins/apm/public/store/reactReduxRequest/errorDistribution';
|
||||||
|
@ -51,20 +49,14 @@ export function ServiceMetrics({ urlParams, location }: ServiceMetricsProps) {
|
||||||
<ErrorDistributionRequest
|
<ErrorDistributionRequest
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
render={({ data }) => (
|
render={({ data }) => (
|
||||||
<Distribution
|
<ErrorDistribution
|
||||||
distribution={data}
|
distribution={data}
|
||||||
title={
|
title={i18n.translate(
|
||||||
<EuiTitle size="xs">
|
'xpack.apm.serviceDetails.metrics.errorOccurrencesChartTitle',
|
||||||
<span>
|
{
|
||||||
{i18n.translate(
|
defaultMessage: 'Error occurrences'
|
||||||
'xpack.apm.serviceDetails.metrics.errorOccurrencesChartTitle',
|
}
|
||||||
{
|
)}
|
||||||
defaultMessage: 'Error occurrences'
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</EuiTitle>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RRRRenderResponse } from 'react-redux-request';
|
import { RRRRenderResponse } from 'react-redux-request';
|
||||||
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
|
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
|
||||||
// @ts-ignore
|
|
||||||
import { TraceListRequest } from '../../../store/reactReduxRequest/traceList';
|
import { TraceListRequest } from '../../../store/reactReduxRequest/traceList';
|
||||||
import { EmptyMessage } from '../../shared/EmptyMessage';
|
import { EmptyMessage } from '../../shared/EmptyMessage';
|
||||||
import { TraceList } from './TraceList';
|
import { TraceList } from './TraceList';
|
||||||
|
|
|
@ -56,7 +56,7 @@ interface Props {
|
||||||
urlParams: IUrlParams;
|
urlParams: IUrlParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Distribution extends Component<Props> {
|
export class TransactionDistribution extends Component<Props> {
|
||||||
public formatYShort = (t: number) => {
|
public formatYShort = (t: number) => {
|
||||||
return i18n.translate(
|
return i18n.translate(
|
||||||
'xpack.apm.transactionDetails.transactionsDurationDistributionChart.unitShortLabel',
|
'xpack.apm.transactionDetails.transactionsDurationDistributionChart.unitShortLabel',
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { px, units } from '../../../../../../../style/variables';
|
import { px, units } from '../../../../../../../style/variables';
|
||||||
// @ts-ignore
|
|
||||||
import { Ellipsis } from '../../../../../../shared/Icons';
|
import { Ellipsis } from '../../../../../../shared/Icons';
|
||||||
|
|
||||||
const ToggleButtonContainer = styled.div`
|
const ToggleButtonContainer = styled.div`
|
||||||
|
@ -59,10 +58,7 @@ export const TruncateHeightSection: React.SFC<Props> = ({
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Ellipsis
|
<Ellipsis horizontal={!isOpen} />{' '}
|
||||||
horizontal={!isOpen}
|
|
||||||
style={{ marginRight: units.half }}
|
|
||||||
/>{' '}
|
|
||||||
{isOpen
|
{isOpen
|
||||||
? i18n.translate('xpack.apm.toggleHeight.showMoreButtonLabel', {
|
? i18n.translate('xpack.apm.toggleHeight.showMoreButtonLabel', {
|
||||||
defaultMessage: 'Show more lines'
|
defaultMessage: 'Show more lines'
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { TransactionCharts } from '../../shared/charts/TransactionCharts';
|
||||||
import { EmptyMessage } from '../../shared/EmptyMessage';
|
import { EmptyMessage } from '../../shared/EmptyMessage';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { KueryBar } from '../../shared/KueryBar';
|
import { KueryBar } from '../../shared/KueryBar';
|
||||||
import { Distribution } from './Distribution';
|
import { TransactionDistribution } from './Distribution';
|
||||||
import { Transaction } from './Transaction';
|
import { Transaction } from './Transaction';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -54,7 +54,7 @@ export function TransactionDetailsView({ urlParams, location }: Props) {
|
||||||
<TransactionDistributionRequest
|
<TransactionDistributionRequest
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
render={({ data }) => (
|
render={({ data }) => (
|
||||||
<Distribution
|
<TransactionDistribution
|
||||||
distribution={data}
|
distribution={data}
|
||||||
urlParams={urlParams}
|
urlParams={urlParams}
|
||||||
location={location}
|
location={location}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 from 'react';
|
|
||||||
|
|
||||||
export function Icon({ name, className, ...props }) {
|
|
||||||
return <i className={`fa ${name} ${className}`} {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Ellipsis({ horizontal, style, ...props }) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
style={{
|
|
||||||
transition: 'transform 0.1s',
|
|
||||||
transform: `rotate(${horizontal ? 90 : 0}deg)`,
|
|
||||||
...style
|
|
||||||
}}
|
|
||||||
name="fa-ellipsis-v"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Check({ ...props }) {
|
|
||||||
return <Icon name="fa-check" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Close({ ...props }) {
|
|
||||||
return <Icon name="fa-times" {...props} />;
|
|
||||||
}
|
|
22
x-pack/plugins/apm/public/components/shared/Icons.tsx
Normal file
22
x-pack/plugins/apm/public/components/shared/Icons.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiIcon } from '@elastic/eui';
|
||||||
|
import React from 'react';
|
||||||
|
import { units } from '../../style/variables';
|
||||||
|
|
||||||
|
export function Ellipsis({ horizontal }: { horizontal: boolean }) {
|
||||||
|
return (
|
||||||
|
<EuiIcon
|
||||||
|
style={{
|
||||||
|
transition: 'transform 0.1s',
|
||||||
|
transform: `rotate(${horizontal ? 90 : 0}deg)`,
|
||||||
|
marginRight: units.half
|
||||||
|
}}
|
||||||
|
type="boxesVertical"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -9,8 +9,6 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { IStackframe } from 'x-pack/plugins/apm/typings/es_schemas/fields/Stackframe';
|
import { IStackframe } from 'x-pack/plugins/apm/typings/es_schemas/fields/Stackframe';
|
||||||
import { units } from '../../../style/variables';
|
|
||||||
// @ts-ignore
|
|
||||||
import { Ellipsis } from '../../shared/Icons';
|
import { Ellipsis } from '../../shared/Icons';
|
||||||
import { Stackframe } from './Stackframe';
|
import { Stackframe } from './Stackframe';
|
||||||
|
|
||||||
|
@ -48,10 +46,7 @@ export class LibraryStackFrames extends React.Component<Props, State> {
|
||||||
<div>
|
<div>
|
||||||
<LibraryFrameToggle>
|
<LibraryFrameToggle>
|
||||||
<EuiLink onClick={this.onClick}>
|
<EuiLink onClick={this.onClick}>
|
||||||
<Ellipsis
|
<Ellipsis horizontal={isVisible} />{' '}
|
||||||
horizontal={isVisible}
|
|
||||||
style={{ marginRight: units.half }}
|
|
||||||
/>{' '}
|
|
||||||
{i18n.translate(
|
{i18n.translate(
|
||||||
'xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel',
|
'xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel',
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,6 @@ import {
|
||||||
unit,
|
unit,
|
||||||
units
|
units
|
||||||
} from '../../../style/variables';
|
} from '../../../style/variables';
|
||||||
// @ts-ignore
|
|
||||||
import { Ellipsis } from '../Icons';
|
import { Ellipsis } from '../Icons';
|
||||||
import { PropertiesTable } from '../PropertiesTable';
|
import { PropertiesTable } from '../PropertiesTable';
|
||||||
|
|
||||||
|
@ -59,10 +58,7 @@ export class Variables extends React.Component<Props> {
|
||||||
return (
|
return (
|
||||||
<VariablesContainer>
|
<VariablesContainer>
|
||||||
<VariablesToggle onClick={this.onClick}>
|
<VariablesToggle onClick={this.onClick}>
|
||||||
<Ellipsis
|
<Ellipsis horizontal={this.state.isVisible} />{' '}
|
||||||
horizontal={this.state.isVisible}
|
|
||||||
style={{ marginRight: units.half }}
|
|
||||||
/>{' '}
|
|
||||||
{i18n.translate(
|
{i18n.translate(
|
||||||
'xpack.apm.stacktraceTab.localVariablesToogleButtonLabel',
|
'xpack.apm.stacktraceTab.localVariablesToogleButtonLabel',
|
||||||
{ defaultMessage: 'Local variables' }
|
{ defaultMessage: 'Local variables' }
|
||||||
|
|
|
@ -10,8 +10,6 @@ import { isEmpty, last } from 'lodash';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { IStackframe } from '../../../../typings/es_schemas/fields/Stackframe';
|
import { IStackframe } from '../../../../typings/es_schemas/fields/Stackframe';
|
||||||
import { EmptyMessage } from '../../shared/EmptyMessage';
|
import { EmptyMessage } from '../../shared/EmptyMessage';
|
||||||
// @ts-ignore
|
|
||||||
import { Ellipsis } from '../../shared/Icons';
|
|
||||||
import { LibraryStackFrames } from './LibraryStackFrames';
|
import { LibraryStackFrames } from './LibraryStackFrames';
|
||||||
import { Stackframe } from './Stackframe';
|
import { Stackframe } from './Stackframe';
|
||||||
|
|
||||||
|
|
|
@ -4,26 +4,28 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules'; // eslint-disable-line no-unused-vars
|
|
||||||
import chrome from 'ui/chrome';
|
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { Router } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import 'ui/autoload/styles';
|
|
||||||
import 'ui/autoload/all';
|
|
||||||
import 'uiExports/autocompleteProviders';
|
|
||||||
import 'react-vis/dist/style.css';
|
import 'react-vis/dist/style.css';
|
||||||
|
import 'ui/autoload/all';
|
||||||
|
import 'ui/autoload/styles';
|
||||||
|
import chrome from 'ui/chrome';
|
||||||
|
import { I18nContext } from 'ui/i18n';
|
||||||
|
// @ts-ignore
|
||||||
|
import { uiModules } from 'ui/modules';
|
||||||
|
import 'uiExports/autocompleteProviders';
|
||||||
|
import { GlobalHelpExtension } from './components/app/GlobalHelpExtension';
|
||||||
|
import { Main } from './components/app/Main';
|
||||||
|
import { GlobalProgress } from './components/app/Main/GlobalProgress';
|
||||||
|
import { history } from './components/shared/Links/url_helpers';
|
||||||
|
// @ts-ignore
|
||||||
|
import configureStore from './store/config/configureStore';
|
||||||
import './style/global_overrides.css';
|
import './style/global_overrides.css';
|
||||||
import template from './templates/index.html';
|
import template from './templates/index.html';
|
||||||
import { Main } from './components/app/Main';
|
// @ts-ignore
|
||||||
import { initTimepicker } from './utils/timepicker';
|
import { initTimepicker } from './utils/timepicker';
|
||||||
import configureStore from './store/config/configureStore';
|
|
||||||
import GlobalProgress from './components/app/Main/GlobalProgress';
|
|
||||||
import { GlobalHelpExtension } from './components/app/GlobalHelpExtension';
|
|
||||||
|
|
||||||
import { history } from './components/shared/Links/url_helpers';
|
|
||||||
import { I18nContext } from 'ui/i18n';
|
|
||||||
|
|
||||||
// render APM feedback link in global help menu
|
// render APM feedback link in global help menu
|
||||||
chrome.helpExtension.set(domElement => {
|
chrome.helpExtension.set(domElement => {
|
||||||
|
@ -33,6 +35,7 @@ chrome.helpExtension.set(domElement => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
chrome.setRootTemplate(template);
|
chrome.setRootTemplate(template);
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
|
@ -4,9 +4,15 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Location } from 'history';
|
||||||
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
export const LOCATION_UPDATE = 'LOCATION_UPDATE';
|
export const LOCATION_UPDATE = 'LOCATION_UPDATE';
|
||||||
|
|
||||||
function location(state = { pathname: '', search: '', hash: '' }, action) {
|
export function locationReducer(
|
||||||
|
state = { pathname: '', search: '', hash: '' },
|
||||||
|
action: AnyAction
|
||||||
|
) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case LOCATION_UPDATE:
|
case LOCATION_UPDATE:
|
||||||
return action.location;
|
return action.location;
|
||||||
|
@ -15,11 +21,9 @@ function location(state = { pathname: '', search: '', hash: '' }, action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateLocation(nextLocation) {
|
export function updateLocation(nextLocation: Location) {
|
||||||
return {
|
return {
|
||||||
type: LOCATION_UPDATE,
|
type: LOCATION_UPDATE,
|
||||||
location: nextLocation
|
location: nextLocation
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default location;
|
|
|
@ -10,7 +10,6 @@ import { ErrorDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/erro
|
||||||
import { loadErrorDistribution } from '../../services/rest/apm/error_groups';
|
import { loadErrorDistribution } from '../../services/rest/apm/error_groups';
|
||||||
import { IReduxState } from '../rootReducer';
|
import { IReduxState } from '../rootReducer';
|
||||||
import { IUrlParams } from '../urlParams';
|
import { IUrlParams } from '../urlParams';
|
||||||
// @ts-ignore
|
|
||||||
import { createInitialDataSelector } from './helpers';
|
import { createInitialDataSelector } from './helpers';
|
||||||
|
|
||||||
const ID = 'errorDistribution';
|
const ID = 'errorDistribution';
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { ErrorGroupAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_
|
||||||
import { loadErrorGroupDetails } from '../../services/rest/apm/error_groups';
|
import { loadErrorGroupDetails } from '../../services/rest/apm/error_groups';
|
||||||
import { IReduxState } from '../rootReducer';
|
import { IReduxState } from '../rootReducer';
|
||||||
import { IUrlParams } from '../urlParams';
|
import { IUrlParams } from '../urlParams';
|
||||||
// @ts-ignore
|
|
||||||
import { createInitialDataSelector } from './helpers';
|
import { createInitialDataSelector } from './helpers';
|
||||||
|
|
||||||
const ID = 'errorGroupDetails';
|
const ID = 'errorGroupDetails';
|
||||||
|
|
|
@ -5,16 +5,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Request } from 'react-redux-request';
|
import { Request, RRRRender } from 'react-redux-request';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
|
||||||
import { loadTraceList } from '../../services/rest/apm/traces';
|
import { loadTraceList } from '../../services/rest/apm/traces';
|
||||||
|
import { IReduxState } from '../rootReducer';
|
||||||
|
import { IUrlParams } from '../urlParams';
|
||||||
import { createInitialDataSelector } from './helpers';
|
import { createInitialDataSelector } from './helpers';
|
||||||
|
|
||||||
const ID = 'traceList';
|
const ID = 'traceList';
|
||||||
const INITIAL_DATA = [];
|
const INITIAL_DATA: TraceListAPIResponse = [];
|
||||||
const withInitialData = createInitialDataSelector(INITIAL_DATA);
|
const withInitialData = createInitialDataSelector(INITIAL_DATA);
|
||||||
|
|
||||||
const selectRRR = (state = {}) => state.reactReduxRequest;
|
const selectRRR = (state = {} as IReduxState) => state.reactReduxRequest;
|
||||||
|
|
||||||
export const selectTraceList = createSelector(
|
export const selectTraceList = createSelector(
|
||||||
[selectRRR],
|
[selectRRR],
|
||||||
|
@ -23,7 +26,12 @@ export const selectTraceList = createSelector(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export function TraceListRequest({ urlParams = {}, render }) {
|
interface Props {
|
||||||
|
urlParams: IUrlParams;
|
||||||
|
render: RRRRender<TraceListAPIResponse>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TraceListRequest({ urlParams, render }: Props) {
|
||||||
const { start, end, kuery } = urlParams;
|
const { start, end, kuery } = urlParams;
|
||||||
|
|
||||||
if (!start || !end) {
|
if (!start || !end) {
|
|
@ -13,8 +13,6 @@ import {
|
||||||
} from '../../components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
|
} from '../../components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
|
||||||
import { loadTrace } from '../../services/rest/apm/traces';
|
import { loadTrace } from '../../services/rest/apm/traces';
|
||||||
import { IUrlParams } from '../urlParams';
|
import { IUrlParams } from '../urlParams';
|
||||||
// @ts-ignore
|
|
||||||
import { createInitialDataSelector } from './helpers';
|
|
||||||
|
|
||||||
export const ID = 'waterfall';
|
export const ID = 'waterfall';
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,7 @@ import { Location } from 'history';
|
||||||
import { reducer } from 'react-redux-request';
|
import { reducer } from 'react-redux-request';
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
import { StringMap } from '../../typings/common';
|
import { StringMap } from '../../typings/common';
|
||||||
// @ts-ignore
|
import { locationReducer } from './location';
|
||||||
import location from './location';
|
|
||||||
import { IUrlParams, urlParamsReducer } from './urlParams';
|
import { IUrlParams, urlParamsReducer } from './urlParams';
|
||||||
|
|
||||||
export interface IReduxState {
|
export interface IReduxState {
|
||||||
|
@ -19,7 +18,7 @@ export interface IReduxState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rootReducer = combineReducers({
|
export const rootReducer = combineReducers({
|
||||||
location,
|
location: locationReducer,
|
||||||
urlParams: urlParamsReducer,
|
urlParams: urlParamsReducer,
|
||||||
reactReduxRequest: reducer
|
reactReduxRequest: reducer
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,14 +4,13 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Location } from 'history';
|
||||||
import { compact, pick } from 'lodash';
|
import { compact, pick } from 'lodash';
|
||||||
import { AnyAction } from 'redux';
|
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import {
|
import {
|
||||||
legacyDecodeURIComponent,
|
legacyDecodeURIComponent,
|
||||||
toQuery
|
toQuery
|
||||||
} from '../components/shared/Links/url_helpers';
|
} from '../components/shared/Links/url_helpers';
|
||||||
// @ts-ignore
|
|
||||||
import { LOCATION_UPDATE } from './location';
|
import { LOCATION_UPDATE } from './location';
|
||||||
import { getDefaultTransactionType } from './reactReduxRequest/serviceDetails';
|
import { getDefaultTransactionType } from './reactReduxRequest/serviceDetails';
|
||||||
import { getDefaultDistributionSample } from './reactReduxRequest/transactionDistribution';
|
import { getDefaultDistributionSample } from './reactReduxRequest/transactionDistribution';
|
||||||
|
@ -20,6 +19,16 @@ import { IReduxState } from './rootReducer';
|
||||||
// ACTION TYPES
|
// ACTION TYPES
|
||||||
export const TIMEPICKER_UPDATE = 'TIMEPICKER_UPDATE';
|
export const TIMEPICKER_UPDATE = 'TIMEPICKER_UPDATE';
|
||||||
|
|
||||||
|
interface LocationAction {
|
||||||
|
type: typeof LOCATION_UPDATE;
|
||||||
|
location: Location;
|
||||||
|
}
|
||||||
|
interface TimepickerAction {
|
||||||
|
type: typeof TIMEPICKER_UPDATE;
|
||||||
|
time: { min: number; max: number };
|
||||||
|
}
|
||||||
|
type Action = LocationAction | TimepickerAction;
|
||||||
|
|
||||||
// "urlParams" contains path and query parameters from the url, that can be easily consumed from
|
// "urlParams" contains path and query parameters from the url, that can be easily consumed from
|
||||||
// any (container) component with access to the store
|
// any (container) component with access to the store
|
||||||
|
|
||||||
|
@ -28,7 +37,7 @@ export const TIMEPICKER_UPDATE = 'TIMEPICKER_UPDATE';
|
||||||
// serviceName: opbeans-backend (path param)
|
// serviceName: opbeans-backend (path param)
|
||||||
// transactionType: Brewing%20Bot (path param)
|
// transactionType: Brewing%20Bot (path param)
|
||||||
// transactionId: 1321 (query param)
|
// transactionId: 1321 (query param)
|
||||||
export function urlParamsReducer(state = {}, action: AnyAction) {
|
export function urlParamsReducer(state = {}, action: Action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case LOCATION_UPDATE: {
|
case LOCATION_UPDATE: {
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { metadata } from 'ui/metadata';
|
import { metadata } from 'ui/metadata';
|
||||||
const STACK_VERSION = metadata.branch;
|
const STACK_VERSION = metadata.branch;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { metadata } from 'ui/metadata';
|
import { metadata } from 'ui/metadata';
|
||||||
const STACK_VERSION = metadata.branch;
|
const STACK_VERSION = metadata.branch;
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,17 @@
|
||||||
|
|
||||||
/* global jest */
|
/* global jest */
|
||||||
|
|
||||||
import { mount } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
import moment from 'moment';
|
|
||||||
import { createMockStore } from 'redux-test-utils';
|
|
||||||
import createHistory from 'history/createHashHistory';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import enzymeToJson from 'enzyme-to-json';
|
import enzymeToJson from 'enzyme-to-json';
|
||||||
|
import createHistory from 'history/createHashHistory';
|
||||||
import 'jest-styled-components';
|
import 'jest-styled-components';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Moment } from 'moment-timezone';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
// @ts-ignore
|
||||||
|
import { createMockStore } from 'redux-test-utils';
|
||||||
|
|
||||||
export function toJson(wrapper) {
|
export function toJson(wrapper: ReactWrapper) {
|
||||||
return enzymeToJson(wrapper, {
|
return enzymeToJson(wrapper, {
|
||||||
noKey: true,
|
noKey: true,
|
||||||
mode: 'deep'
|
mode: 'deep'
|
||||||
|
@ -27,7 +29,7 @@ const defaultRoute = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function mountWithRouterAndStore(
|
export function mountWithRouterAndStore(
|
||||||
Component,
|
Component: React.ReactElement,
|
||||||
storeState = {},
|
storeState = {},
|
||||||
route = defaultRoute
|
route = defaultRoute
|
||||||
) {
|
) {
|
||||||
|
@ -51,7 +53,7 @@ export function mountWithRouterAndStore(
|
||||||
return mount(Component, options);
|
return mount(Component, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mountWithStore(Component, storeState = {}) {
|
export function mountWithStore(Component: React.ReactElement, storeState = {}) {
|
||||||
const store = createMockStore(storeState);
|
const store = createMockStore(storeState);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -68,12 +70,16 @@ export function mountWithStore(Component, storeState = {}) {
|
||||||
|
|
||||||
export function mockMoment() {
|
export function mockMoment() {
|
||||||
// avoid timezone issues
|
// avoid timezone issues
|
||||||
jest.spyOn(moment.prototype, 'format').mockImplementation(function() {
|
jest
|
||||||
return `1st of January (mocking ${this.unix()})`;
|
.spyOn(moment.prototype, 'format')
|
||||||
});
|
.mockImplementation(function(this: Moment) {
|
||||||
|
return `1st of January (mocking ${this.unix()})`;
|
||||||
|
});
|
||||||
|
|
||||||
// convert relative time to absolute time to avoid timing issues
|
// convert relative time to absolute time to avoid timing issues
|
||||||
jest.spyOn(moment.prototype, 'fromNow').mockImplementation(function() {
|
jest
|
||||||
return `1337 minutes ago (mocking ${this.unix()})`;
|
.spyOn(moment.prototype, 'fromNow')
|
||||||
});
|
.mockImplementation(function(this: Moment) {
|
||||||
|
return `1337 minutes ago (mocking ${this.unix()})`;
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -4,13 +4,15 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { SearchParams } from 'elasticsearch';
|
||||||
import { OBSERVER_LISTENING } from '../../../common/elasticsearch_fieldnames';
|
import { OBSERVER_LISTENING } from '../../../common/elasticsearch_fieldnames';
|
||||||
|
import { Setup } from '../helpers/setup_request';
|
||||||
|
|
||||||
// Note: this logic is duplicated in tutorials/apm/envs/on_prem
|
// Note: this logic is duplicated in tutorials/apm/envs/on_prem
|
||||||
export async function getServerStatus({ setup }) {
|
export async function getServerStatus({ setup }: { setup: Setup }) {
|
||||||
const { client, config } = setup;
|
const { client, config } = setup;
|
||||||
|
|
||||||
const params = {
|
const params: SearchParams = {
|
||||||
index: config.get('apm_oss.onboardingIndices'),
|
index: config.get('apm_oss.onboardingIndices'),
|
||||||
body: {
|
body: {
|
||||||
size: 0,
|
size: 0,
|
|
@ -4,26 +4,28 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Joi from 'joi';
|
|
||||||
import Boom from 'boom';
|
import Boom from 'boom';
|
||||||
|
import { Server } from 'hapi';
|
||||||
|
import Joi from 'joi';
|
||||||
|
import { Legacy } from 'kibana';
|
||||||
import { getDistribution } from '../lib/errors/distribution/get_distribution';
|
import { getDistribution } from '../lib/errors/distribution/get_distribution';
|
||||||
import { getErrorGroups } from '../lib/errors/get_error_groups';
|
|
||||||
import { getErrorGroup } from '../lib/errors/get_error_group';
|
import { getErrorGroup } from '../lib/errors/get_error_group';
|
||||||
import { setupRequest } from '../lib/helpers/setup_request';
|
import { getErrorGroups } from '../lib/errors/get_error_groups';
|
||||||
import { withDefaultValidators } from '../lib/helpers/input_validation';
|
import { withDefaultValidators } from '../lib/helpers/input_validation';
|
||||||
|
import { setupRequest } from '../lib/helpers/setup_request';
|
||||||
|
|
||||||
const ROOT = '/api/apm/services/{serviceName}/errors';
|
const ROOT = '/api/apm/services/{serviceName}/errors';
|
||||||
const defaultErrorHandler = err => {
|
const defaultErrorHandler = (err: Error) => {
|
||||||
|
// tslint:disable-next-line
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
throw Boom.boomify(err, { statusCode: 400 });
|
throw Boom.boomify(err, { statusCode: 400 });
|
||||||
};
|
};
|
||||||
|
|
||||||
export function initErrorsApi(server) {
|
export function initErrorsApi(server: Server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: ROOT,
|
path: ROOT,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: withDefaultValidators({
|
query: withDefaultValidators({
|
||||||
sortField: Joi.string(),
|
sortField: Joi.string(),
|
||||||
|
@ -34,7 +36,10 @@ export function initErrorsApi(server) {
|
||||||
handler: req => {
|
handler: req => {
|
||||||
const setup = setupRequest(req);
|
const setup = setupRequest(req);
|
||||||
const { serviceName } = req.params;
|
const { serviceName } = req.params;
|
||||||
const { sortField, sortDirection } = req.query;
|
const { sortField, sortDirection } = req.query as {
|
||||||
|
sortField: string;
|
||||||
|
sortDirection: 'desc' | 'asc';
|
||||||
|
};
|
||||||
|
|
||||||
return getErrorGroups({
|
return getErrorGroups({
|
||||||
serviceName,
|
serviceName,
|
||||||
|
@ -48,7 +53,7 @@ export function initErrorsApi(server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${ROOT}/{groupId}`,
|
path: `${ROOT}/{groupId}`,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: withDefaultValidators()
|
query: withDefaultValidators()
|
||||||
}
|
}
|
||||||
|
@ -62,19 +67,19 @@ export function initErrorsApi(server) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const distributionHandler = req => {
|
function distributionHandler(req: Legacy.Request) {
|
||||||
const setup = setupRequest(req);
|
const setup = setupRequest(req);
|
||||||
const { serviceName, groupId } = req.params;
|
const { serviceName, groupId } = req.params;
|
||||||
|
|
||||||
return getDistribution({ serviceName, groupId, setup }).catch(
|
return getDistribution({ serviceName, groupId, setup }).catch(
|
||||||
defaultErrorHandler
|
defaultErrorHandler
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${ROOT}/{groupId}/distribution`,
|
path: `${ROOT}/{groupId}/distribution`,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: withDefaultValidators()
|
query: withDefaultValidators()
|
||||||
}
|
}
|
||||||
|
@ -85,7 +90,7 @@ export function initErrorsApi(server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${ROOT}/distribution`,
|
path: `${ROOT}/distribution`,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: withDefaultValidators()
|
query: withDefaultValidators()
|
||||||
}
|
}
|
|
@ -4,23 +4,25 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Joi from 'joi';
|
|
||||||
import Boom from 'boom';
|
import Boom from 'boom';
|
||||||
import { getServerStatus } from '../lib/status_check/server_check';
|
import { Server } from 'hapi';
|
||||||
import { getAgentStatus } from '../lib/status_check/agent_check';
|
import Joi from 'joi';
|
||||||
import { setupRequest } from '../lib/helpers/setup_request';
|
import { setupRequest } from '../lib/helpers/setup_request';
|
||||||
|
import { getAgentStatus } from '../lib/status_check/agent_check';
|
||||||
|
import { getServerStatus } from '../lib/status_check/server_check';
|
||||||
|
|
||||||
const ROOT = '/api/apm/status';
|
const ROOT = '/api/apm/status';
|
||||||
const defaultErrorHandler = err => {
|
const defaultErrorHandler = (err: Error) => {
|
||||||
|
// tslint:disable-next-line
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
throw Boom.boomify(err, { statusCode: 400 });
|
throw Boom.boomify(err, { statusCode: 400 });
|
||||||
};
|
};
|
||||||
|
|
||||||
export function initStatusApi(server) {
|
export function initStatusApi(server: Server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${ROOT}/server`,
|
path: `${ROOT}/server`,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: Joi.object().keys({
|
query: Joi.object().keys({
|
||||||
_debug: Joi.bool()
|
_debug: Joi.bool()
|
||||||
|
@ -36,7 +38,7 @@ export function initStatusApi(server) {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${ROOT}/agent`,
|
path: `${ROOT}/agent`,
|
||||||
config: {
|
options: {
|
||||||
validate: {
|
validate: {
|
||||||
query: Joi.object().keys({
|
query: Joi.object().keys({
|
||||||
_debug: Joi.bool()
|
_debug: Joi.bool()
|
Loading…
Add table
Add a link
Reference in a new issue