Skip to content

Commit

Permalink
Migrate status page app to core (#72017)
Browse files Browse the repository at this point in the history
* move http.anonymousPaths.register('/status'); logic into core, remove status_page plugin

* move status_page to core & migrate lib

* migrate the status_app components to TS/KP APIs

* update rendering snapshots

* use import type syntax

* moves `/status` server-side route to core

* fix route registration

* update generated file

* change statusPage i18n prefix

* (temporary) try to restore legacy plugin to check behavior

* add some FTR tests

* do not import whole lodash

* update snapshots

* review comments

* improve / clean component unit tests

* change url for legacy status app

* set status app as chromeless

* fix FTR test due to chromeless app

* fix typings

* add unit test for CoreApp /status registration
  • Loading branch information
pgayvallet authored Jul 23, 2020
1 parent b2a473d commit 2178a14
Show file tree
Hide file tree
Showing 56 changed files with 1,064 additions and 830 deletions.
5 changes: 0 additions & 5 deletions docs/developer/architecture/code-exploration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,6 @@ WARNING: Missing README.
Replaces the legacy ui/share module for registering share context menus.
- {kib-repo}blob/{branch}/src/plugins/status_page[statusPage]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry]
Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things:
Expand Down
32 changes: 25 additions & 7 deletions src/core/public/core_app/core_app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ import {
AppNavLinkStatus,
AppMountParameters,
} from '../application';
import { HttpSetup, HttpStart } from '../http';
import { CoreContext } from '../core_system';
import { renderApp, setupUrlOverflowDetection } from './errors';
import { NotificationsStart } from '../notifications';
import { IUiSettingsClient } from '../ui_settings';
import type { HttpSetup, HttpStart } from '../http';
import type { CoreContext } from '../core_system';
import type { NotificationsSetup, NotificationsStart } from '../notifications';
import type { IUiSettingsClient } from '../ui_settings';
import type { InjectedMetadataSetup } from '../injected_metadata';
import { renderApp as renderErrorApp, setupUrlOverflowDetection } from './errors';
import { renderApp as renderStatusApp } from './status';

interface SetupDeps {
application: InternalApplicationSetup;
http: HttpSetup;
injectedMetadata: InjectedMetadataSetup;
notifications: NotificationsSetup;
}

interface StartDeps {
Expand All @@ -47,7 +51,7 @@ export class CoreApp {

constructor(private readonly coreContext: CoreContext) {}

public setup({ http, application }: SetupDeps) {
public setup({ http, application, injectedMetadata, notifications }: SetupDeps) {
application.register(this.coreContext.coreId, {
id: 'error',
title: 'App Error',
Expand All @@ -56,7 +60,21 @@ export class CoreApp {
// Do not use an async import here in order to ensure that network failures
// cannot prevent the error UI from displaying. This UI is tiny so an async
// import here is probably not useful anyways.
return renderApp(params, { basePath: http.basePath });
return renderErrorApp(params, { basePath: http.basePath });
},
});

if (injectedMetadata.getAnonymousStatusPage()) {
http.anonymousPaths.register('/status');
}
application.register(this.coreContext.coreId, {
id: 'status',
title: 'Server Status',
appRoute: '/status',
chromeless: true,
navLinkStatus: AppNavLinkStatus.hidden,
mount(params: AppMountParameters) {
return renderStatusApp(params, { http, notifications });
},
});
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
* under the License.
*/

import { PluginInitializer } from 'kibana/public';
import { StatusPagePlugin, StatusPagePluginSetup, StatusPagePluginStart } from './plugin';

export const plugin: PluginInitializer<StatusPagePluginSetup, StatusPagePluginStart> = () =>
new StatusPagePlugin();
export { MetricTile, MetricTiles } from './metric_tiles';
export { ServerStatus } from './server_status';
export { StatusTable } from './status_table';
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,50 @@
import React from 'react';
import { shallow } from 'enzyme';
import { MetricTile } from './metric_tiles';
import { Metric } from '../lib';

const GENERAL_METRIC = {
const untypedMetric: Metric = {
name: 'A metric',
value: 1.8,
// no type specified
};

const BYTE_METRIC = {
const byteMetric: Metric = {
name: 'Heap Total',
value: 1501560832,
type: 'byte',
};

const FLOAT_METRIC = {
const floatMetric: Metric = {
name: 'Load',
type: 'float',
value: [4.0537109375, 3.36669921875, 3.1220703125],
};

const MS_METRIC = {
const timeMetric: Metric = {
name: 'Response Time Max',
type: 'ms',
type: 'time',
value: 1234,
};

test('general metric', () => {
const component = shallow(<MetricTile metric={GENERAL_METRIC} />);
expect(component).toMatchSnapshot();
});
describe('MetricTile', () => {
it('correct displays an untyped metric', () => {
const component = shallow(<MetricTile metric={untypedMetric} />);
expect(component).toMatchSnapshot();
});

test('byte metric', () => {
const component = shallow(<MetricTile metric={BYTE_METRIC} />);
expect(component).toMatchSnapshot();
});
it('correct displays a byte metric', () => {
const component = shallow(<MetricTile metric={byteMetric} />);
expect(component).toMatchSnapshot();
});

test('float metric', () => {
const component = shallow(<MetricTile metric={FLOAT_METRIC} />);
expect(component).toMatchSnapshot();
});
it('correct displays a float metric', () => {
const component = shallow(<MetricTile metric={floatMetric} />);
expect(component).toMatchSnapshot();
});

test('millisecond metric', () => {
const component = shallow(<MetricTile metric={MS_METRIC} />);
expect(component).toMatchSnapshot();
it('correct displays a time metric', () => {
const component = shallow(<MetricTile metric={timeMetric} />);
expect(component).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,43 @@
* under the License.
*/

import formatNumber from '../lib/format_number';
import React, { Component } from 'react';
import { Metric as MetricPropType } from '../lib/prop_types';
import PropTypes from 'prop-types';
import React, { FunctionComponent } from 'react';
import { EuiFlexGrid, EuiFlexItem, EuiCard } from '@elastic/eui';
import { formatNumber, Metric } from '../lib';

/*
Displays a metric with the correct format.
*/
export class MetricTile extends Component {
static propTypes = {
metric: MetricPropType.isRequired,
};

formattedMetric() {
const { value, type } = this.props.metric;

const metrics = [].concat(value);
return metrics
.map(function (metric) {
return formatNumber(metric, type);
})
.join(', ');
}

render() {
const { name } = this.props.metric;

return <EuiCard layout="horizontal" title={this.formattedMetric()} description={name} />;
}
}
* Displays a metric with the correct format.
*/
export const MetricTile: FunctionComponent<{ metric: Metric }> = ({ metric }) => {
const { name } = metric;
return (
<EuiCard
data-test-subj={`serverMetric-${formatMetricId(metric)}`}
layout="horizontal"
title={formatMetric(metric)}
description={name}
/>
);
};

/*
Wrapper component that simply maps each metric to MetricTile inside a FlexGroup
*/
const MetricTiles = ({ metrics }) => (
* Wrapper component that simply maps each metric to MetricTile inside a FlexGroup
*/
export const MetricTiles: FunctionComponent<{ metrics: Metric[] }> = ({ metrics }) => (
<EuiFlexGrid columns={3}>
{metrics.map((metric) => (
<EuiFlexItem key={metric.name}>
<EuiFlexItem key={metric.name} data-test-subj="serverMetric">
<MetricTile metric={metric} />
</EuiFlexItem>
))}
</EuiFlexGrid>
);

MetricTiles.propTypes = {
metrics: PropTypes.arrayOf(MetricPropType).isRequired,
const formatMetric = ({ value, type }: Metric) => {
const metrics = Array.isArray(value) ? value : [value];
return metrics.map((metric) => formatNumber(metric, type)).join(', ');
};

export default MetricTiles;
const formatMetricId = ({ name }: Metric) => {
return name.toLowerCase().replace(/[ ]+/g, '-');
};
Loading

0 comments on commit 2178a14

Please sign in to comment.