mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
This commit is contained in:
parent
4237e7f184
commit
2998d9c971
10 changed files with 225 additions and 7 deletions
|
@ -8,6 +8,7 @@
|
|||
|
||||
```typescript
|
||||
readonly links: {
|
||||
readonly settings: string;
|
||||
readonly canvas: {
|
||||
readonly guide: string;
|
||||
};
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -18,8 +18,13 @@ 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 renderErrorApp,
|
||||
setupPublicBaseUrlConfigWarning,
|
||||
setupUrlOverflowDetection,
|
||||
} from './errors';
|
||||
import { renderApp as renderStatusApp } from './status';
|
||||
import { DocLinksStart } from '../doc_links';
|
||||
|
||||
interface SetupDeps {
|
||||
application: InternalApplicationSetup;
|
||||
|
@ -30,6 +35,7 @@ interface SetupDeps {
|
|||
|
||||
interface StartDeps {
|
||||
application: InternalApplicationStart;
|
||||
docLinks: DocLinksStart;
|
||||
http: HttpStart;
|
||||
notifications: NotificationsStart;
|
||||
uiSettings: IUiSettingsClient;
|
||||
|
@ -40,7 +46,7 @@ export class CoreApp {
|
|||
|
||||
constructor(private readonly coreContext: CoreContext) {}
|
||||
|
||||
public setup({ http, application, injectedMetadata, notifications }: SetupDeps) {
|
||||
public setup({ application, http, injectedMetadata, notifications }: SetupDeps) {
|
||||
application.register(this.coreContext.coreId, {
|
||||
id: 'error',
|
||||
title: 'App Error',
|
||||
|
@ -68,7 +74,7 @@ export class CoreApp {
|
|||
});
|
||||
}
|
||||
|
||||
public start({ application, http, notifications, uiSettings }: StartDeps) {
|
||||
public start({ application, docLinks, http, notifications, uiSettings }: StartDeps) {
|
||||
if (!application.history) {
|
||||
return;
|
||||
}
|
||||
|
@ -79,6 +85,8 @@ export class CoreApp {
|
|||
toasts: notifications.toasts,
|
||||
uiSettings,
|
||||
});
|
||||
|
||||
setupPublicBaseUrlConfigWarning({ docLinks, http, notifications });
|
||||
}
|
||||
|
||||
public stop() {
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
|
||||
export { renderApp } from './error_application';
|
||||
export { setupUrlOverflowDetection, URL_MAX_LENGTH } from './url_overflow';
|
||||
export { setupPublicBaseUrlConfigWarning } from './public_base_url';
|
||||
|
|
114
src/core/public/core_app/errors/public_base_url.test.tsx
Normal file
114
src/core/public/core_app/errors/public_base_url.test.tsx
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { docLinksServiceMock } from '../../doc_links/doc_links_service.mock';
|
||||
import { httpServiceMock } from '../../http/http_service.mock';
|
||||
import { notificationServiceMock } from '../../notifications/notifications_service.mock';
|
||||
|
||||
import { setupPublicBaseUrlConfigWarning } from './public_base_url';
|
||||
|
||||
describe('publicBaseUrl warning', () => {
|
||||
const docLinks = docLinksServiceMock.createStartContract();
|
||||
const notifications = notificationServiceMock.createStartContract();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('does not show any toast on localhost', () => {
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
setupPublicBaseUrlConfigWarning({
|
||||
docLinks,
|
||||
notifications,
|
||||
http,
|
||||
location: {
|
||||
hostname: 'localhost',
|
||||
} as Location,
|
||||
});
|
||||
|
||||
expect(notifications.toasts.addWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not show any toast on 127.0.0.1', () => {
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
setupPublicBaseUrlConfigWarning({
|
||||
docLinks,
|
||||
notifications,
|
||||
http,
|
||||
location: {
|
||||
hostname: '127.0.0.1',
|
||||
} as Location,
|
||||
});
|
||||
|
||||
expect(notifications.toasts.addWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not show toast if configured correctly', () => {
|
||||
const http = httpServiceMock.createStartContract({ publicBaseUrl: 'http://myhost.com' });
|
||||
|
||||
setupPublicBaseUrlConfigWarning({
|
||||
docLinks,
|
||||
notifications,
|
||||
http,
|
||||
location: {
|
||||
hostname: 'myhost.com',
|
||||
toString() {
|
||||
return 'http://myhost.com/';
|
||||
},
|
||||
} as Location,
|
||||
});
|
||||
|
||||
expect(notifications.toasts.addWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('config missing toast', () => {
|
||||
it('adds toast if publicBaseUrl is missing', () => {
|
||||
const http = httpServiceMock.createStartContract({ publicBaseUrl: undefined });
|
||||
|
||||
setupPublicBaseUrlConfigWarning({
|
||||
docLinks,
|
||||
notifications,
|
||||
http,
|
||||
location: {
|
||||
hostname: 'myhost.com',
|
||||
toString() {
|
||||
return 'http://myhost.com/';
|
||||
},
|
||||
} as Location,
|
||||
});
|
||||
|
||||
expect(notifications.toasts.addWarning).toHaveBeenCalledWith({
|
||||
title: 'Configuration missing',
|
||||
text: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it('does not add toast if storage key set', () => {
|
||||
const http = httpServiceMock.createStartContract({ publicBaseUrl: undefined });
|
||||
|
||||
setupPublicBaseUrlConfigWarning({
|
||||
docLinks,
|
||||
notifications,
|
||||
http,
|
||||
location: {
|
||||
hostname: 'myhost.com',
|
||||
toString() {
|
||||
return 'http://myhost.com/';
|
||||
},
|
||||
} as Location,
|
||||
storage: {
|
||||
getItem: (id: string) => 'true',
|
||||
} as Storage,
|
||||
});
|
||||
|
||||
expect(notifications.toasts.addWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
88
src/core/public/core_app/errors/public_base_url.tsx
Normal file
88
src/core/public/core_app/errors/public_base_url.tsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import type { HttpStart, NotificationsStart } from '../..';
|
||||
import type { DocLinksStart } from '../../doc_links';
|
||||
import { mountReactNode } from '../../utils';
|
||||
|
||||
/** Only exported for tests */
|
||||
export const MISSING_CONFIG_STORAGE_KEY = `core.warnings.publicBaseUrlMissingDismissed`;
|
||||
|
||||
interface Deps {
|
||||
docLinks: DocLinksStart;
|
||||
http: HttpStart;
|
||||
notifications: NotificationsStart;
|
||||
// Exposed for easier testing
|
||||
storage?: Storage;
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
export const setupPublicBaseUrlConfigWarning = ({
|
||||
docLinks,
|
||||
http,
|
||||
notifications,
|
||||
storage = window.localStorage,
|
||||
location = window.location,
|
||||
}: Deps) => {
|
||||
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
||||
return;
|
||||
}
|
||||
|
||||
const missingWarningSeen = storage.getItem(MISSING_CONFIG_STORAGE_KEY) === 'true';
|
||||
if (missingWarningSeen || http.basePath.publicBaseUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toast = notifications.toasts.addWarning({
|
||||
title: i18n.translate('core.ui.publicBaseUrlWarning.configMissingTitle', {
|
||||
defaultMessage: 'Configuration missing',
|
||||
}),
|
||||
text: mountReactNode(
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="core.ui.publicBaseUrlWarning.configMissingDescription"
|
||||
defaultMessage="{configKey} is missing and should be configured when running in a production environment. Some features may not behave correctly."
|
||||
values={{
|
||||
configKey: <code>server.publicBaseUrl</code>,
|
||||
}}
|
||||
/>{' '}
|
||||
<a href={`${docLinks.links.settings}#server-publicBaseUrl`} target="_blank">
|
||||
<FormattedMessage
|
||||
id="core.ui.publicBaseUrlWarning.seeDocumentationLinkLabel"
|
||||
defaultMessage="See the documentation."
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
size="s"
|
||||
onClick={() => {
|
||||
notifications.toasts.remove(toast);
|
||||
storage.setItem(MISSING_CONFIG_STORAGE_KEY, 'true');
|
||||
}}
|
||||
id="mute"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="core.ui.publicBaseUrlWarning.muteWarningButtonLabel"
|
||||
defaultMessage="Mute warning"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
),
|
||||
});
|
||||
};
|
|
@ -202,7 +202,7 @@ export class CoreSystem {
|
|||
});
|
||||
const deprecations = this.deprecations.start({ http });
|
||||
|
||||
this.coreApp.start({ application, http, notifications, uiSettings });
|
||||
this.coreApp.start({ application, docLinks, http, notifications, uiSettings });
|
||||
|
||||
const core: InternalCoreStart = {
|
||||
application,
|
||||
|
|
|
@ -29,6 +29,7 @@ export class DocLinksService {
|
|||
DOC_LINK_VERSION,
|
||||
ELASTIC_WEBSITE_URL,
|
||||
links: {
|
||||
settings: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/settings.html`,
|
||||
canvas: {
|
||||
guide: `${KIBANA_DOCS}canvas.html`,
|
||||
},
|
||||
|
@ -428,6 +429,7 @@ export interface DocLinksStart {
|
|||
readonly DOC_LINK_VERSION: string;
|
||||
readonly ELASTIC_WEBSITE_URL: string;
|
||||
readonly links: {
|
||||
readonly settings: string;
|
||||
readonly canvas: {
|
||||
readonly guide: string;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,10 @@ export type HttpSetupMock = jest.Mocked<HttpSetup> & {
|
|||
anonymousPaths: jest.Mocked<HttpSetup['anonymousPaths']>;
|
||||
};
|
||||
|
||||
const createServiceMock = ({ basePath = '' } = {}): HttpSetupMock => ({
|
||||
const createServiceMock = ({
|
||||
basePath = '',
|
||||
publicBaseUrl,
|
||||
}: { basePath?: string; publicBaseUrl?: string } = {}): HttpSetupMock => ({
|
||||
fetch: jest.fn(),
|
||||
get: jest.fn(),
|
||||
head: jest.fn(),
|
||||
|
@ -27,7 +30,7 @@ const createServiceMock = ({ basePath = '' } = {}): HttpSetupMock => ({
|
|||
patch: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
options: jest.fn(),
|
||||
basePath: new BasePath(basePath),
|
||||
basePath: new BasePath(basePath, undefined, publicBaseUrl),
|
||||
anonymousPaths: {
|
||||
register: jest.fn(),
|
||||
isAnonymous: jest.fn(),
|
||||
|
|
|
@ -487,6 +487,7 @@ export interface DocLinksStart {
|
|||
readonly ELASTIC_WEBSITE_URL: string;
|
||||
// (undocumented)
|
||||
readonly links: {
|
||||
readonly settings: string;
|
||||
readonly canvas: {
|
||||
readonly guide: string;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue