Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ba9e213
move http.anonymousPaths.register('/status'); logic into core, remove…
pgayvallet Jul 16, 2020
5964ffc
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 16, 2020
f63d8d2
move status_page to core & migrate lib
pgayvallet Jul 16, 2020
dcc1f0a
migrate the status_app components to TS/KP APIs
pgayvallet Jul 16, 2020
6b42c45
update rendering snapshots
pgayvallet Jul 16, 2020
854211e
use import type syntax
pgayvallet Jul 16, 2020
c4dbd19
moves `/status` server-side route to core
pgayvallet Jul 16, 2020
7f927f7
fix route registration
pgayvallet Jul 16, 2020
c857b74
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 17, 2020
8a2f98e
update generated file
pgayvallet Jul 17, 2020
0a7b243
change statusPage i18n prefix
pgayvallet Jul 20, 2020
d65c125
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 20, 2020
7bdc3c2
(temporary) try to restore legacy plugin to check behavior
pgayvallet Jul 20, 2020
e91d5b7
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 21, 2020
b7bc230
add some FTR tests
pgayvallet Jul 21, 2020
4beb5d3
do not import whole lodash
pgayvallet Jul 21, 2020
66337af
update snapshots
pgayvallet Jul 21, 2020
46c5adf
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 22, 2020
716dee7
review comments
pgayvallet Jul 22, 2020
92924b7
improve / clean component unit tests
pgayvallet Jul 22, 2020
7352ad9
change url for legacy status app
pgayvallet Jul 22, 2020
3a3e2e9
set status app as chromeless
pgayvallet Jul 22, 2020
5b8719e
fix FTR test due to chromeless app
pgayvallet Jul 22, 2020
1a0c2ae
fix typings
pgayvallet Jul 22, 2020
920f52b
add unit test for CoreApp /status registration
pgayvallet Jul 22, 2020
71f4273
Merge remote-tracking branch 'upstream/master' into kbn-67979-status-…
pgayvallet Jul 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot navigate to the other Kibana pages from other anonymous pages without reloading. It creates a bug when status.allowAnonymous: true and you logged-in user lands on the /status page and navigates to other pages. result: SecurityNavControl is not rendered.
2020-07-22_10-50-55
As a workaround, we could make status page chromeless to enforce page reloading.
Is it possible to land on the /status page from a link in Kibana UI?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot navigate to the other Kibana pages from other anonymous pages without reloading. It creates a bug when status.allowAnonymous: true and you logged-in user lands on the /status page and navigates to other pages. result: SecurityNavControl is not rendered.

Hum. This indeed is an issue. We really need to have a way to know about anonymous vs logged-in apps from core, to be able to automatically performs that kind of reload when using navigateToApp between anon/logged or logged/anon apps. Do you know if we have an issue for that?

As a workaround, we could make status page chromeless to enforce page reloading.

chromeless apps do not really force page reloading, but I guess hiding the chrome navigation will remove any way for the user to navigate away from the status page using the UI. do you think it would be sufficient?

Is it possible to land on the /status page from a link in Kibana UI?

Not that I'm aware of. The app is hidden and there is no link to it anywhere in chrome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I guess hiding the chrome navigation will remove any way for the user to navigate away from the status page using the UI.

that was my reasoning as well. I think it's okay since the user navigates to the /status page manually.

Hum. This indeed is an issue. We really need to have a way to know about anonymous vs logged-in apps from core, to be able to automatically performs that kind of reload when using navigateToApp between anon/logged or logged/anon apps. Do you know if we have an issue for that?

I'm not aware of any.

}
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 });
Comment on lines +76 to +77
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We choose to not use async import for the error app, so I think it makes sense to do the same for the status page.

},
});
}
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