Use documentation links service provided by the core directly. (#88158)

This commit is contained in:
Aleh Zasypkin 2021-01-15 13:31:51 +01:00 committed by GitHub
parent 91a23b7df0
commit abfd8bb9d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 504 additions and 692 deletions

View file

@ -108,12 +108,38 @@ readonly links: {
readonly ml: Record<string, string>; readonly ml: Record<string, string>;
readonly transforms: Record<string, string>; readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>; readonly visualize: Record<string, string>;
readonly apis: Record<string, string>; readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>; readonly observability: Record<string, string>;
readonly alerting: Record<string, string>; readonly alerting: Record<string, string>;
readonly maps: Record<string, string>; readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>; readonly monitoring: Record<string, string>;
readonly security: Record<string, string>; readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>; readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>; readonly ccs: Record<string, string>;
}; };

File diff suppressed because one or more lines are too long

View file

@ -220,7 +220,7 @@ export class DocLinksService {
}, },
apis: { apis: {
createIndex: `${ELASTICSEARCH_DOCS}indices-create-index.html`, createIndex: `${ELASTICSEARCH_DOCS}indices-create-index.html`,
createSnapshotLifecylePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`, createSnapshotLifecyclePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`,
createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`, createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`,
createRoleMappingTemplates: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html#_role_templates`, createRoleMappingTemplates: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html#_role_templates`,
createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`, createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`,
@ -344,12 +344,38 @@ export interface DocLinksStart {
readonly ml: Record<string, string>; readonly ml: Record<string, string>;
readonly transforms: Record<string, string>; readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>; readonly visualize: Record<string, string>;
readonly apis: Record<string, string>; readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>; readonly observability: Record<string, string>;
readonly alerting: Record<string, string>; readonly alerting: Record<string, string>;
readonly maps: Record<string, string>; readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>; readonly monitoring: Record<string, string>;
readonly security: Record<string, string>; readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>; readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>; readonly ccs: Record<string, string>;
}; };

View file

@ -587,12 +587,38 @@ export interface DocLinksStart {
readonly ml: Record<string, string>; readonly ml: Record<string, string>;
readonly transforms: Record<string, string>; readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>; readonly visualize: Record<string, string>;
readonly apis: Record<string, string>; readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>; readonly observability: Record<string, string>;
readonly alerting: Record<string, string>; readonly alerting: Record<string, string>;
readonly maps: Record<string, string>; readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>; readonly monitoring: Record<string, string>;
readonly security: Record<string, string>; readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>; readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>; readonly ccs: Record<string, string>;
}; };

View file

@ -1,132 +1,123 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`APIKeysGridPage renders a callout when API keys are not enabled 1`] = ` exports[`APIKeysGridPage renders a callout when API keys are not enabled 1`] = `
<NotEnabled <EuiCallOut
docLinks={ color="danger"
DocumentationLinksService { iconType="alert"
"apiKeySettings": "https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings", title={
"createApiKey": "https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-create-api-key.html", <FormattedMessage
} defaultMessage="API keys not enabled in Elasticsearch"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle"
values={Object {}}
/>
} }
> >
<EuiCallOut <div
color="danger" className="euiCallOut euiCallOut--danger"
iconType="alert"
title={
<FormattedMessage
defaultMessage="API keys not enabled in Elasticsearch"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle"
values={Object {}}
/>
}
> >
<div <div
className="euiCallOut euiCallOut--danger" className="euiCallOutHeader"
> >
<div <EuiIcon
className="euiCallOutHeader" aria-hidden="true"
className="euiCallOutHeader__icon"
size="m"
type="alert"
> >
<EuiIcon <span
aria-hidden="true" aria-hidden="true"
className="euiCallOutHeader__icon" className="euiCallOutHeader__icon"
data-euiicon-type="alert"
size="m" size="m"
type="alert" />
> </EuiIcon>
<span <span
aria-hidden="true" className="euiCallOutHeader__title"
className="euiCallOutHeader__icon"
data-euiicon-type="alert"
size="m"
/>
</EuiIcon>
<span
className="euiCallOutHeader__title"
>
<FormattedMessage
defaultMessage="API keys not enabled in Elasticsearch"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle"
values={Object {}}
>
API keys not enabled in Elasticsearch
</FormattedMessage>
</span>
</div>
<EuiText
size="s"
> >
<div <FormattedMessage
className="euiText euiText--small" defaultMessage="API keys not enabled in Elasticsearch"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle"
values={Object {}}
> >
<FormattedMessage API keys not enabled in Elasticsearch
defaultMessage="Contact your system administrator and refer to the {link} to enable API keys." </FormattedMessage>
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription" </span>
values={ </div>
Object { <EuiText
"link": <EuiLink size="s"
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings" >
target="_blank" <div
> className="euiText euiText--small"
<FormattedMessage >
defaultMessage="docs" <FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText" defaultMessage="Contact your system administrator and refer to the {link} to enable API keys."
values={Object {}} id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription"
/> values={
</EuiLink>, Object {
} "link": <EuiLink
}
>
Contact your system administrator and refer to the
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings"
target="_blank"
>
<a
className="euiLink euiLink--primary"
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings" href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings"
rel="noopener"
target="_blank" target="_blank"
> >
<FormattedMessage <FormattedMessage
defaultMessage="docs" defaultMessage="docs"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText" id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText"
values={Object {}} values={Object {}}
> />
docs </EuiLink>,
</FormattedMessage> }
<EuiIcon }
>
Contact your system administrator and refer to the
<EuiLink
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings"
target="_blank"
>
<a
className="euiLink euiLink--primary"
href="https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings"
rel="noopener"
target="_blank"
>
<FormattedMessage
defaultMessage="docs"
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText"
values={Object {}}
>
docs
</FormattedMessage>
<EuiIcon
aria-label="External link"
className="euiLink__externalIcon"
size="s"
type="popout"
>
<span
aria-label="External link" aria-label="External link"
className="euiLink__externalIcon" className="euiLink__externalIcon"
data-euiicon-type="popout"
size="s" size="s"
type="popout" />
</EuiIcon>
<EuiScreenReaderOnly>
<span
className="euiScreenReaderOnly"
> >
<span <EuiI18n
aria-label="External link" default="(opens in a new tab or window)"
className="euiLink__externalIcon" token="euiLink.newTarget.screenReaderOnlyText"
data-euiicon-type="popout"
size="s"
/>
</EuiIcon>
<EuiScreenReaderOnly>
<span
className="euiScreenReaderOnly"
> >
<EuiI18n (opens in a new tab or window)
default="(opens in a new tab or window)" </EuiI18n>
token="euiLink.newTarget.screenReaderOnlyText" </span>
> </EuiScreenReaderOnly>
(opens in a new tab or window) </a>
</EuiI18n> </EuiLink>
</span> to enable API keys.
</EuiScreenReaderOnly> </FormattedMessage>
</a> </div>
</EuiLink> </EuiText>
to enable API keys. </div>
</FormattedMessage> </EuiCallOut>
</div>
</EuiText>
</div>
</EuiCallOut>
</NotEnabled>
`; `;
exports[`APIKeysGridPage renders permission denied if user does not have required permissions 1`] = ` exports[`APIKeysGridPage renders permission denied if user does not have required permissions 1`] = `

View file

@ -10,10 +10,10 @@ import { ReactWrapper } from 'enzyme';
import { EuiCallOut } from '@elastic/eui'; import { EuiCallOut } from '@elastic/eui';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
import { NotEnabled } from './not_enabled'; import { NotEnabled } from './not_enabled';
import { PermissionDenied } from './permission_denied'; import { PermissionDenied } from './permission_denied';
import { APIKeysAPIClient } from '../api_keys_api_client'; import { APIKeysAPIClient } from '../api_keys_api_client';
import { DocumentationLinksService } from '../documentation_links';
import { APIKeysGridPage } from './api_keys_grid_page'; import { APIKeysGridPage } from './api_keys_grid_page';
import { coreMock } from '../../../../../../../src/core/public/mocks'; import { coreMock } from '../../../../../../../src/core/public/mocks';
@ -66,21 +66,16 @@ describe('APIKeysGridPage', () => {
}); });
const coreStart = coreMock.createStart(); const coreStart = coreMock.createStart();
const renderView = () => {
const getViewProperties = () => { return mountWithIntl(
const { docLinks, notifications, application } = coreStart; <KibanaContextProvider services={coreStart}>
return { <APIKeysGridPage apiKeysAPIClient={apiClientMock} notifications={coreStart.notifications} />
docLinks: new DocumentationLinksService(docLinks), </KibanaContextProvider>
navigateToApp: application.navigateToApp, );
notifications,
apiKeysAPIClient: apiClientMock,
};
}; };
it('renders a loading state when fetching API keys', async () => { it('renders a loading state when fetching API keys', async () => {
const wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); expect(renderView().find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1);
expect(wrapper.find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1);
}); });
it('renders a callout when API keys are not enabled', async () => { it('renders a callout when API keys are not enabled', async () => {
@ -90,13 +85,12 @@ describe('APIKeysGridPage', () => {
areApiKeysEnabled: false, areApiKeysEnabled: false,
}); });
const wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); const wrapper = renderView();
await waitForRender(wrapper, (updatedWrapper) => { await waitForRender(wrapper, (updatedWrapper) => {
return updatedWrapper.find(NotEnabled).length > 0; return updatedWrapper.find(NotEnabled).length > 0;
}); });
expect(wrapper.find(NotEnabled)).toMatchSnapshot(); expect(wrapper.find(NotEnabled).find(EuiCallOut)).toMatchSnapshot();
}); });
it('renders permission denied if user does not have required permissions', async () => { it('renders permission denied if user does not have required permissions', async () => {
@ -106,8 +100,7 @@ describe('APIKeysGridPage', () => {
areApiKeysEnabled: true, areApiKeysEnabled: true,
}); });
const wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); const wrapper = renderView();
await waitForRender(wrapper, (updatedWrapper) => { await waitForRender(wrapper, (updatedWrapper) => {
return updatedWrapper.find(PermissionDenied).length > 0; return updatedWrapper.find(PermissionDenied).length > 0;
}); });
@ -118,8 +111,7 @@ describe('APIKeysGridPage', () => {
it('renders error callout if error fetching API keys', async () => { it('renders error callout if error fetching API keys', async () => {
apiClientMock.getApiKeys.mockRejectedValue(mock500()); apiClientMock.getApiKeys.mockRejectedValue(mock500());
const wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); const wrapper = renderView();
await waitForRender(wrapper, (updatedWrapper) => { await waitForRender(wrapper, (updatedWrapper) => {
return updatedWrapper.find(EuiCallOut).length > 0; return updatedWrapper.find(EuiCallOut).length > 0;
}); });
@ -130,7 +122,7 @@ describe('APIKeysGridPage', () => {
describe('Admin view', () => { describe('Admin view', () => {
let wrapper: ReactWrapper<any>; let wrapper: ReactWrapper<any>;
beforeEach(() => { beforeEach(() => {
wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); wrapper = renderView();
}); });
it('renders a callout indicating the user is an administrator', async () => { it('renders a callout indicating the user is an administrator', async () => {
@ -165,7 +157,7 @@ describe('APIKeysGridPage', () => {
areApiKeysEnabled: true, areApiKeysEnabled: true,
}); });
wrapper = mountWithIntl(<APIKeysGridPage {...getViewProperties()} />); wrapper = renderView();
}); });
it('does NOT render a callout indicating the user is an administrator', async () => { it('does NOT render a callout indicating the user is an administrator', async () => {

View file

@ -28,11 +28,10 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import moment from 'moment-timezone'; import moment from 'moment-timezone';
import { ApplicationStart, NotificationsStart } from 'src/core/public'; import type { NotificationsStart } from 'src/core/public';
import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public'; import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public';
import { ApiKey, ApiKeyToInvalidate } from '../../../../common/model'; import { ApiKey, ApiKeyToInvalidate } from '../../../../common/model';
import { APIKeysAPIClient } from '../api_keys_api_client'; import { APIKeysAPIClient } from '../api_keys_api_client';
import { DocumentationLinksService } from '../documentation_links';
import { PermissionDenied } from './permission_denied'; import { PermissionDenied } from './permission_denied';
import { EmptyPrompt } from './empty_prompt'; import { EmptyPrompt } from './empty_prompt';
import { NotEnabled } from './not_enabled'; import { NotEnabled } from './not_enabled';
@ -40,9 +39,7 @@ import { InvalidateProvider } from './invalidate_provider';
interface Props { interface Props {
notifications: NotificationsStart; notifications: NotificationsStart;
docLinks: DocumentationLinksService;
apiKeysAPIClient: PublicMethodsOf<APIKeysAPIClient>; apiKeysAPIClient: PublicMethodsOf<APIKeysAPIClient>;
navigateToApp: ApplicationStart['navigateToApp'];
} }
interface State { interface State {
@ -132,7 +129,7 @@ export class APIKeysGridPage extends Component<Props, State> {
if (!areApiKeysEnabled) { if (!areApiKeysEnabled) {
return ( return (
<EuiPageContent> <EuiPageContent>
<NotEnabled docLinks={this.props.docLinks} /> <NotEnabled />
</EuiPageContent> </EuiPageContent>
); );
} }
@ -140,11 +137,7 @@ export class APIKeysGridPage extends Component<Props, State> {
if (!isLoadingTable && apiKeys && apiKeys.length === 0) { if (!isLoadingTable && apiKeys && apiKeys.length === 0) {
return ( return (
<EuiPageContent> <EuiPageContent>
<EmptyPrompt <EmptyPrompt isAdmin={isAdmin} />
isAdmin={isAdmin}
docLinks={this.props.docLinks}
navigateToApp={this.props.navigateToApp}
/>
</EuiPageContent> </EuiPageContent>
); );
} }

View file

@ -5,72 +5,70 @@
*/ */
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { ApplicationStart } from 'kibana/public';
import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui'; import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { DocumentationLinksService } from '../../documentation_links'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
interface Props { interface Props {
isAdmin: boolean; isAdmin: boolean;
docLinks: DocumentationLinksService;
navigateToApp: ApplicationStart['navigateToApp'];
} }
export const EmptyPrompt: React.FunctionComponent<Props> = ({ export const EmptyPrompt: React.FunctionComponent<Props> = ({ isAdmin }) => {
isAdmin, const { services } = useKibana();
docLinks, const application = services.application!;
navigateToApp, const docLinks = services.docLinks!;
}) => ( return (
<EuiEmptyPrompt <EuiEmptyPrompt
iconType="managementApp" iconType="managementApp"
title={ title={
<h1 data-test-subj="noApiKeysHeader"> <h1 data-test-subj="noApiKeysHeader">
{isAdmin ? ( {isAdmin ? (
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptAdminTitle"
defaultMessage="No API keys"
/>
) : (
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle"
defaultMessage="You don't have any API keys"
/>
)}
</h1>
}
body={
<Fragment>
<p>
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptDescription"
defaultMessage="You can create an {link} from Console."
values={{
link: (
<EuiLink href={docLinks.links.apis.createApiKey} target="_blank">
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage"
defaultMessage="API key"
/>
</EuiLink>
),
}}
/>
</p>
</Fragment>
}
actions={
<EuiButton
type="primary"
onClick={() => application.navigateToApp('dev_tools')}
data-test-subj="goToConsoleButton"
>
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptAdminTitle" id="xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage"
defaultMessage="No API keys" defaultMessage="Go to Console"
/> />
) : ( </EuiButton>
<FormattedMessage }
id="xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle" data-test-subj="emptyPrompt"
defaultMessage="You don't have any API keys" />
/> );
)} };
</h1>
}
body={
<Fragment>
<p>
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptDescription"
defaultMessage="You can create an {link} from Console."
values={{
link: (
<EuiLink href={`${docLinks.getCreateApiKeyDocUrl()}`} target="_blank">
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage"
defaultMessage="API key"
/>
</EuiLink>
),
}}
/>
</p>
</Fragment>
}
actions={
<EuiButton
type="primary"
onClick={() => navigateToApp('dev_tools')}
data-test-subj="goToConsoleButton"
>
<FormattedMessage
id="xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage"
defaultMessage="Go to Console"
/>
</EuiButton>
}
data-test-subj="emptyPrompt"
/>
);

View file

@ -7,36 +7,35 @@
import React from 'react'; import React from 'react';
import { EuiCallOut, EuiLink } from '@elastic/eui'; import { EuiCallOut, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { DocumentationLinksService } from '../../documentation_links'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
interface Props { export const NotEnabled: React.FunctionComponent = () => {
docLinks: DocumentationLinksService; const docLinks = useKibana().services.docLinks!;
} return (
<EuiCallOut
export const NotEnabled: React.FunctionComponent<Props> = ({ docLinks }) => ( title={
<EuiCallOut <FormattedMessage
title={ id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle"
defaultMessage="API keys not enabled in Elasticsearch"
/>
}
color="danger"
iconType="alert"
>
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle" id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription"
defaultMessage="API keys not enabled in Elasticsearch" defaultMessage="Contact your system administrator and refer to the {link} to enable API keys."
values={{
link: (
<EuiLink href={`${docLinks.links.security.apiKeyServiceSettings}`} target="_blank">
<FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText"
defaultMessage="docs"
/>
</EuiLink>
),
}}
/> />
} </EuiCallOut>
color="danger" );
iconType="alert" };
>
<FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription"
defaultMessage="Contact your system administrator and refer to the {link} to enable API keys."
values={{
link: (
<EuiLink href={`${docLinks.getApiKeyServiceSettingsDocUrl()}`} target="_blank">
<FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText"
defaultMessage="docs"
/>
</EuiLink>
),
}}
/>
</EuiCallOut>
);

View file

@ -43,7 +43,7 @@ describe('apiKeysManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '/', text: 'API Keys' }]); expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '/', text: 'API Keys' }]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Page: {"notifications":{"toasts":{}},"docLinks":{"apiKeySettings":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings","createApiKey":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-create-api-key.html"},"apiKeysAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}}} Page: {"notifications":{"toasts":{}},"apiKeysAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}}}
</div> </div>
`); `);

View file

@ -9,8 +9,8 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { StartServicesAccessor } from 'src/core/public'; import { StartServicesAccessor } from 'src/core/public';
import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import { PluginStartDependencies } from '../../plugin'; import { PluginStartDependencies } from '../../plugin';
import { DocumentationLinksService } from './documentation_links';
interface CreateParams { interface CreateParams {
getStartServices: StartServicesAccessor<PluginStartDependencies>; getStartServices: StartServicesAccessor<PluginStartDependencies>;
@ -35,25 +35,21 @@ export const apiKeysManagementApp = Object.freeze({
}, },
]); ]);
const [ const [[core], { APIKeysGridPage }, { APIKeysAPIClient }] = await Promise.all([
[{ docLinks, http, notifications, i18n: i18nStart, application }],
{ APIKeysGridPage },
{ APIKeysAPIClient },
] = await Promise.all([
getStartServices(), getStartServices(),
import('./api_keys_grid'), import('./api_keys_grid'),
import('./api_keys_api_client'), import('./api_keys_api_client'),
]); ]);
render( render(
<i18nStart.Context> <KibanaContextProvider services={core}>
<APIKeysGridPage <core.i18n.Context>
navigateToApp={application.navigateToApp} <APIKeysGridPage
notifications={notifications} notifications={core.notifications}
docLinks={new DocumentationLinksService(docLinks)} apiKeysAPIClient={new APIKeysAPIClient(core.http)}
apiKeysAPIClient={new APIKeysAPIClient(http)} />
/> </core.i18n.Context>
</i18nStart.Context>, </KibanaContextProvider>,
element element
); );

View file

@ -1,25 +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 { DocLinksStart } from 'src/core/public';
export class DocumentationLinksService {
private readonly apiKeySettings: string;
private readonly createApiKey: string;
constructor(docLinks: DocLinksStart) {
this.apiKeySettings = `${docLinks.links.security.apiKeyServiceSettings}`;
this.createApiKey = `${docLinks.links.apis.createApiKey}`;
}
public getApiKeyServiceSettingsDocUrl() {
return `${this.apiKeySettings}`;
}
public getCreateApiKeyDocUrl() {
return `${this.createApiKey}`;
}
}

View file

@ -7,36 +7,35 @@
import React from 'react'; import React from 'react';
import { EuiCallOut, EuiLink } from '@elastic/eui'; import { EuiCallOut, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { DocumentationLinksService } from '../../documentation_links'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
interface Props { export const NoCompatibleRealms: React.FunctionComponent = () => {
docLinks: DocumentationLinksService; const docLinks = useKibana().services.docLinks!;
} return (
<EuiCallOut
export const NoCompatibleRealms: React.FunctionComponent<Props> = ({ docLinks }: Props) => ( title={
<EuiCallOut <FormattedMessage
title={ id="xpack.security.management.roleMappings.noCompatibleRealmsErrorTitle"
defaultMessage="No compatible realms are enabled in Elasticsearch"
/>
}
color="warning"
iconType="alert"
>
<FormattedMessage <FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorTitle" id="xpack.security.management.roleMappings.noCompatibleRealmsErrorDescription"
defaultMessage="No compatible realms are enabled in Elasticsearch" defaultMessage="Role mappings will not be applied to any users. Contact your system administrator and refer to the {link} for more information."
values={{
link: (
<EuiLink href={docLinks.links.security.mappingRoles} external target="_blank">
<FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorLinkText"
defaultMessage="docs"
/>
</EuiLink>
),
}}
/> />
} </EuiCallOut>
color="warning" );
iconType="alert" };
>
<FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorDescription"
defaultMessage="Role mappings will not be applied to any users. Contact your system administrator and refer to the {link} for more information."
values={{
link: (
<EuiLink href={docLinks.getRoleMappingDocUrl()} external={true} target="_blank">
<FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorLinkText"
defaultMessage="docs"
/>
</EuiLink>
),
}}
/>
</EuiCallOut>
);

View file

@ -1,37 +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 { DocLinksStart } from 'src/core/public';
export class DocumentationLinksService {
private readonly mappingRoles: string;
private readonly createRoleMapping: string;
private readonly createRoleMappingTemplates: string;
private readonly roleMappingFieldRules: string;
constructor(docLinks: DocLinksStart) {
this.mappingRoles = `${docLinks.links.security.mappingRoles}`;
this.createRoleMapping = `${docLinks.links.apis.createRoleMapping}`;
this.createRoleMappingTemplates = `${docLinks.links.apis.createRoleMappingTemplates}`;
this.roleMappingFieldRules = `${docLinks.links.security.mappingRolesFieldRules}`;
}
public getRoleMappingDocUrl() {
return `${this.mappingRoles}`;
}
public getRoleMappingAPIDocUrl() {
return `${this.createRoleMapping}`;
}
public getRoleMappingTemplateDocUrl() {
return `${this.createRoleMappingTemplates}`;
}
public getRoleMappingFieldRulesDocUrl() {
return `${this.roleMappingFieldRules}`;
}
}

View file

@ -20,7 +20,7 @@ import { VisualRuleEditor } from './rule_editor_panel/visual_rule_editor';
import { JSONRuleEditor } from './rule_editor_panel/json_rule_editor'; import { JSONRuleEditor } from './rule_editor_panel/json_rule_editor';
import { RolesAPIClient } from '../../roles'; import { RolesAPIClient } from '../../roles';
import { Role } from '../../../../common/model'; import { Role } from '../../../../common/model';
import { DocumentationLinksService } from '../documentation_links'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks';
import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock';
@ -31,6 +31,25 @@ describe('EditRoleMappingPage', () => {
const history = scopedHistoryMock.create(); const history = scopedHistoryMock.create();
let rolesAPI: PublicMethodsOf<RolesAPIClient>; let rolesAPI: PublicMethodsOf<RolesAPIClient>;
const renderView = (
roleMappingsAPI: ReturnType<typeof roleMappingsAPIClientMock.create>,
name?: string
) => {
const coreStart = coreMock.createStart();
return mountWithIntl(
<KibanaContextProvider services={coreStart}>
<EditRoleMappingPage
name={name}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={coreStart.notifications}
docLinks={coreStart.docLinks}
history={history}
/>
</KibanaContextProvider>
);
};
beforeEach(() => { beforeEach(() => {
rolesAPI = rolesAPIClientMock.create(); rolesAPI = rolesAPIClientMock.create();
(rolesAPI as jest.Mocked<RolesAPIClient>).getRoles.mockResolvedValue([ (rolesAPI as jest.Mocked<RolesAPIClient>).getRoles.mockResolvedValue([
@ -50,17 +69,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<EditRoleMappingPage
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -112,18 +121,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name="foo"
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -161,16 +159,7 @@ describe('EditRoleMappingPage', () => {
hasCompatibleRealms: true, hasCompatibleRealms: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<EditRoleMappingPage
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(SectionLoading)).toHaveLength(1);
expect(wrapper.find(PermissionDenied)).toHaveLength(0); expect(wrapper.find(PermissionDenied)).toHaveLength(0);
@ -189,16 +178,7 @@ describe('EditRoleMappingPage', () => {
hasCompatibleRealms: false, hasCompatibleRealms: false,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<EditRoleMappingPage
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(SectionLoading)).toHaveLength(1);
expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
@ -226,18 +206,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name={'foo'}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
expect(findTestSubject(wrapper, 'deprecatedRolesAssigned')).toHaveLength(0); expect(findTestSubject(wrapper, 'deprecatedRolesAssigned')).toHaveLength(0);
await nextTick(); await nextTick();
@ -267,18 +236,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: false, canUseStoredScripts: false,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name={'foo'}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
@ -310,18 +268,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name={'foo'}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
@ -365,18 +312,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name={'foo'}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -421,18 +357,7 @@ describe('EditRoleMappingPage', () => {
canUseStoredScripts: true, canUseStoredScripts: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, 'foo');
const wrapper = mountWithIntl(
<EditRoleMappingPage
name={'foo'}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();

View file

@ -20,7 +20,7 @@ import {
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import { NotificationsStart, ScopedHistory } from 'src/core/public'; import type { NotificationsStart, ScopedHistory, DocLinksStart } from 'src/core/public';
import { RoleMapping } from '../../../../common/model'; import { RoleMapping } from '../../../../common/model';
import { RuleEditorPanel } from './rule_editor_panel'; import { RuleEditorPanel } from './rule_editor_panel';
import { import {
@ -32,7 +32,6 @@ import {
import { RolesAPIClient } from '../../roles'; import { RolesAPIClient } from '../../roles';
import { validateRoleMappingForSave } from './services/role_mapping_validation'; import { validateRoleMappingForSave } from './services/role_mapping_validation';
import { MappingInfoPanel } from './mapping_info_panel'; import { MappingInfoPanel } from './mapping_info_panel';
import { DocumentationLinksService } from '../documentation_links';
import { RoleMappingsAPIClient } from '../role_mappings_api_client'; import { RoleMappingsAPIClient } from '../role_mappings_api_client';
interface State { interface State {
@ -54,7 +53,7 @@ interface Props {
roleMappingsAPI: PublicMethodsOf<RoleMappingsAPIClient>; roleMappingsAPI: PublicMethodsOf<RoleMappingsAPIClient>;
rolesAPIClient: PublicMethodsOf<RolesAPIClient>; rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
notifications: NotificationsStart; notifications: NotificationsStart;
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
history: ScopedHistory; history: ScopedHistory;
} }
@ -163,7 +162,7 @@ export class EditRoleMappingPage extends Component<Props, State> {
values={{ values={{
learnMoreLink: ( learnMoreLink: (
<EuiLink <EuiLink
href={this.props.docLinks.getRoleMappingDocUrl()} href={this.props.docLinks.links.security.mappingRoles}
external={true} external={true}
target="_blank" target="_blank"
> >
@ -180,7 +179,7 @@ export class EditRoleMappingPage extends Component<Props, State> {
{!this.state.hasCompatibleRealms && ( {!this.state.hasCompatibleRealms && (
<> <>
<EuiSpacer size="s" /> <EuiSpacer size="s" />
<NoCompatibleRealms docLinks={this.props.docLinks} /> <NoCompatibleRealms />
</> </>
)} )}
</Fragment> </Fragment>

View file

@ -10,7 +10,6 @@ import { mountWithIntl } from '@kbn/test/jest';
import { findTestSubject } from '@kbn/test/jest'; import { findTestSubject } from '@kbn/test/jest';
import { Role, RoleMapping } from '../../../../../common/model'; import { Role, RoleMapping } from '../../../../../common/model';
import { RolesAPIClient } from '../../../roles'; import { RolesAPIClient } from '../../../roles';
import { DocumentationLinksService } from '../../documentation_links';
import { RoleSelector } from '../role_selector'; import { RoleSelector } from '../role_selector';
import { RoleTemplateEditor } from '../role_selector/role_template_editor'; import { RoleTemplateEditor } from '../role_selector/role_template_editor';
import { MappingInfoPanel } from '.'; import { MappingInfoPanel } from '.';
@ -39,7 +38,7 @@ describe('MappingInfoPanel', () => {
metadata: {}, metadata: {},
} as RoleMapping, } as RoleMapping,
mode: 'create', mode: 'create',
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props']; } as MappingInfoPanel['props'];
@ -86,7 +85,7 @@ describe('MappingInfoPanel', () => {
metadata: {}, metadata: {},
} as RoleMapping, } as RoleMapping,
mode: 'edit', mode: 'edit',
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props']; } as MappingInfoPanel['props'];
@ -112,7 +111,7 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: true, canUseInlineScripts: true,
canUseStoredScripts: false, canUseStoredScripts: false,
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
}; };
@ -153,7 +152,7 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: false, canUseInlineScripts: false,
canUseStoredScripts: true, canUseStoredScripts: true,
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
}; };
@ -194,7 +193,7 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: false, canUseInlineScripts: false,
canUseStoredScripts: false, canUseStoredScripts: false,
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
}; };
@ -219,7 +218,7 @@ describe('MappingInfoPanel', () => {
metadata: {}, metadata: {},
} as RoleMapping, } as RoleMapping,
mode: 'edit', mode: 'edit',
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
rolesAPIClient: rolesAPI, rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props']; } as MappingInfoPanel['props'];

View file

@ -20,6 +20,7 @@ import {
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import type { DocLinksStart } from 'src/core/public';
import { RoleMapping } from '../../../../../common/model'; import { RoleMapping } from '../../../../../common/model';
import { RolesAPIClient } from '../../../roles'; import { RolesAPIClient } from '../../../roles';
import { import {
@ -28,7 +29,6 @@ import {
validateRoleMappingRoleTemplates, validateRoleMappingRoleTemplates,
} from '../services/role_mapping_validation'; } from '../services/role_mapping_validation';
import { RoleSelector } from '../role_selector'; import { RoleSelector } from '../role_selector';
import { DocumentationLinksService } from '../../documentation_links';
interface Props { interface Props {
roleMapping: RoleMapping; roleMapping: RoleMapping;
@ -38,7 +38,7 @@ interface Props {
canUseInlineScripts: boolean; canUseInlineScripts: boolean;
canUseStoredScripts: boolean; canUseStoredScripts: boolean;
rolesAPIClient: PublicMethodsOf<RolesAPIClient>; rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
} }
interface State { interface State {
@ -205,7 +205,7 @@ export class MappingInfoPanel extends Component<Props, State> {
defaultMessage="Create templates that describe the roles to assign to your users." defaultMessage="Create templates that describe the roles to assign to your users."
/>{' '} />{' '}
<EuiLink <EuiLink
href={this.props.docLinks.getRoleMappingTemplateDocUrl()} href={this.props.docLinks.links.apis.createRoleMappingTemplates}
external={true} external={true}
target="_blank" target="_blank"
> >

View file

@ -17,20 +17,24 @@ import { act } from 'react-dom/test-utils';
import { mountWithIntl } from '@kbn/test/jest'; import { mountWithIntl } from '@kbn/test/jest';
import { JSONRuleEditor } from './json_rule_editor'; import { JSONRuleEditor } from './json_rule_editor';
import { EuiCodeEditor } from '@elastic/eui'; import { EuiCodeEditor } from '@elastic/eui';
import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public';
import { AllRule, AnyRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../model'; import { AllRule, AnyRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../model';
import { DocumentationLinksService } from '../../documentation_links';
import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('JSONRuleEditor', () => { describe('JSONRuleEditor', () => {
const renderView = (props: React.ComponentProps<typeof JSONRuleEditor>) => {
const coreStart = coreMock.createStart();
return mountWithIntl(
<KibanaContextProvider services={coreStart}>
<JSONRuleEditor {...props} />
</KibanaContextProvider>
);
};
it('renders an empty rule set', () => { it('renders an empty rule set', () => {
const props = { const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() };
rules: null, const wrapper = renderView(props);
onChange: jest.fn(),
onValidityChange: jest.fn(),
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl(<JSONRuleEditor {...props} />);
expect(props.onChange).not.toHaveBeenCalled(); expect(props.onChange).not.toHaveBeenCalled();
expect(props.onValidityChange).not.toHaveBeenCalled(); expect(props.onValidityChange).not.toHaveBeenCalled();
@ -50,9 +54,8 @@ describe('JSONRuleEditor', () => {
]), ]),
onChange: jest.fn(), onChange: jest.fn(),
onValidityChange: jest.fn(), onValidityChange: jest.fn(),
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
}; };
const wrapper = mountWithIntl(<JSONRuleEditor {...props} />); const wrapper = renderView(props);
const { value } = wrapper.find(EuiCodeEditor).props(); const { value } = wrapper.find(EuiCodeEditor).props();
expect(JSON.parse(value as string)).toEqual({ expect(JSON.parse(value as string)).toEqual({
@ -80,13 +83,8 @@ describe('JSONRuleEditor', () => {
}); });
it('notifies when input contains invalid JSON', () => { it('notifies when input contains invalid JSON', () => {
const props = { const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() };
rules: null, const wrapper = renderView(props);
onChange: jest.fn(),
onValidityChange: jest.fn(),
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl(<JSONRuleEditor {...props} />);
const allRule = JSON.stringify(new AllRule().toRaw()); const allRule = JSON.stringify(new AllRule().toRaw());
act(() => { act(() => {
@ -99,13 +97,8 @@ describe('JSONRuleEditor', () => {
}); });
it('notifies when input contains an invalid rule set, even if it is valid JSON', () => { it('notifies when input contains an invalid rule set, even if it is valid JSON', () => {
const props = { const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() };
rules: null, const wrapper = renderView(props);
onChange: jest.fn(),
onValidityChange: jest.fn(),
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl(<JSONRuleEditor {...props} />);
const invalidRule = JSON.stringify({ const invalidRule = JSON.stringify({
all: [ all: [
@ -127,13 +120,8 @@ describe('JSONRuleEditor', () => {
}); });
it('fires onChange when a valid rule set is provided after being previously invalidated', () => { it('fires onChange when a valid rule set is provided after being previously invalidated', () => {
const props = { const props = { rules: null, onChange: jest.fn(), onValidityChange: jest.fn() };
rules: null, const wrapper = renderView(props);
onChange: jest.fn(),
onValidityChange: jest.fn(),
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl(<JSONRuleEditor {...props} />);
const allRule = JSON.stringify(new AllRule().toRaw()); const allRule = JSON.stringify(new AllRule().toRaw());
act(() => { act(() => {

View file

@ -11,17 +11,17 @@ import 'brace/theme/github';
import { EuiCodeEditor, EuiFormRow, EuiButton, EuiSpacer, EuiLink, EuiText } from '@elastic/eui'; import { EuiCodeEditor, EuiFormRow, EuiButton, EuiSpacer, EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { DocumentationLinksService } from '../../documentation_links';
import { Rule, RuleBuilderError, generateRulesFromRaw } from '../../model'; import { Rule, RuleBuilderError, generateRulesFromRaw } from '../../model';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
interface Props { interface Props {
rules: Rule | null; rules: Rule | null;
onChange: (updatedRules: Rule | null) => void; onChange: (updatedRules: Rule | null) => void;
onValidityChange: (isValid: boolean) => void; onValidityChange: (isValid: boolean) => void;
docLinks: DocumentationLinksService;
} }
export const JSONRuleEditor = (props: Props) => { export const JSONRuleEditor = (props: Props) => {
const docLinks = useKibana().services.docLinks!;
const [rawRules, setRawRules] = useState( const [rawRules, setRawRules] = useState(
JSON.stringify(props.rules ? props.rules.toRaw() : {}, null, 2) JSON.stringify(props.rules ? props.rules.toRaw() : {}, null, 2)
); );
@ -108,7 +108,7 @@ export const JSONRuleEditor = (props: Props) => {
values={{ values={{
roleMappingAPI: ( roleMappingAPI: (
<EuiLink <EuiLink
href={props.docLinks.getRoleMappingAPIDocUrl()} href={docLinks.links.apis.createRoleMapping}
external={true} external={true}
target="_blank" target="_blank"
> >

View file

@ -17,20 +17,28 @@ import '@kbn/test/target/jest/utils/stub_web_worker';
import { AllRule, FieldRule } from '../../model'; import { AllRule, FieldRule } from '../../model';
import { EuiErrorBoundary } from '@elastic/eui'; import { EuiErrorBoundary } from '@elastic/eui';
import { DocumentationLinksService } from '../../documentation_links'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public';
import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('RuleEditorPanel', () => { describe('RuleEditorPanel', () => {
const renderView = (props: Omit<React.ComponentProps<typeof RuleEditorPanel>, 'docLinks'>) => {
const coreStart = coreMock.createStart();
const viewProps = { ...props, docLinks: coreStart.docLinks };
return mountWithIntl(
<KibanaContextProvider services={coreStart}>
<RuleEditorPanel {...viewProps} />
</KibanaContextProvider>
);
};
it('renders the visual editor when no rules are defined', () => { it('renders the visual editor when no rules are defined', () => {
const props = { const props = {
rawRules: {}, rawRules: {},
onChange: jest.fn(), onChange: jest.fn(),
onValidityChange: jest.fn(), onValidityChange: jest.fn(),
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
}; };
const wrapper = mountWithIntl(<RuleEditorPanel {...props} />); const wrapper = renderView(props);
expect(wrapper.find(VisualRuleEditor)).toHaveLength(1); expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
expect(wrapper.find(JSONRuleEditor)).toHaveLength(0); expect(wrapper.find(JSONRuleEditor)).toHaveLength(0);
}); });
@ -49,9 +57,9 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(), onChange: jest.fn(),
onValidityChange: jest.fn(), onValidityChange: jest.fn(),
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks), docLinks: coreMock.createStart().docLinks,
}; };
const wrapper = mountWithIntl(<RuleEditorPanel {...props} />); const wrapper = renderView(props);
expect(wrapper.find(VisualRuleEditor)).toHaveLength(1); expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
expect(wrapper.find(JSONRuleEditor)).toHaveLength(0); expect(wrapper.find(JSONRuleEditor)).toHaveLength(0);
@ -73,9 +81,8 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(), onChange: jest.fn(),
onValidityChange: jest.fn(), onValidityChange: jest.fn(),
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
}; };
const wrapper = mountWithIntl(<RuleEditorPanel {...props} />); const wrapper = renderView(props);
findTestSubject(wrapper, 'roleMappingsJSONRuleEditorButton').simulate('click'); findTestSubject(wrapper, 'roleMappingsJSONRuleEditorButton').simulate('click');
@ -109,9 +116,8 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(), onChange: jest.fn(),
onValidityChange: jest.fn(), onValidityChange: jest.fn(),
validateForm: false, validateForm: false,
docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
}; };
const wrapper = mountWithIntl(<RuleEditorPanel {...props} />); const wrapper = renderView(props);
wrapper.find(VisualRuleEditor).simulateError(new Error('Something awful happened here.')); wrapper.find(VisualRuleEditor).simulateError(new Error('Something awful happened here.'));

View file

@ -22,12 +22,12 @@ import {
} from '@elastic/eui'; } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import type { DocLinksStart } from 'src/core/public';
import { RoleMapping } from '../../../../../common/model'; import { RoleMapping } from '../../../../../common/model';
import { VisualRuleEditor } from './visual_rule_editor'; import { VisualRuleEditor } from './visual_rule_editor';
import { JSONRuleEditor } from './json_rule_editor'; import { JSONRuleEditor } from './json_rule_editor';
import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants'; import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants';
import { Rule, generateRulesFromRaw } from '../../model'; import { Rule, generateRulesFromRaw } from '../../model';
import { DocumentationLinksService } from '../../documentation_links';
import { validateRoleMappingRules } from '../services/role_mapping_validation'; import { validateRoleMappingRules } from '../services/role_mapping_validation';
interface Props { interface Props {
@ -35,7 +35,7 @@ interface Props {
onChange: (rawRules: RoleMapping['rules']) => void; onChange: (rawRules: RoleMapping['rules']) => void;
onValidityChange: (isValid: boolean) => void; onValidityChange: (isValid: boolean) => void;
validateForm: boolean; validateForm: boolean;
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
} }
interface State { interface State {
@ -92,7 +92,7 @@ export class RuleEditorPanel extends Component<Props, State> {
values={{ values={{
learnMoreLink: ( learnMoreLink: (
<EuiLink <EuiLink
href={this.props.docLinks.getRoleMappingFieldRulesDocUrl()} href={this.props.docLinks.links.security.mappingRolesFieldRules}
target="_blank" target="_blank"
external={true} external={true}
> >
@ -215,7 +215,6 @@ export class RuleEditorPanel extends Component<Props, State> {
rules={this.state.rules} rules={this.state.rules}
onChange={this.onRuleChange} onChange={this.onRuleChange}
onValidityChange={this.onValidityChange} onValidityChange={this.onValidityChange}
docLinks={this.props.docLinks}
/> />
); );
default: default:

View file

@ -13,7 +13,7 @@ import { EmptyPrompt } from './empty_prompt';
import { findTestSubject } from '@kbn/test/jest'; import { findTestSubject } from '@kbn/test/jest';
import { EuiLink } from '@elastic/eui'; import { EuiLink } from '@elastic/eui';
import { act } from '@testing-library/react'; import { act } from '@testing-library/react';
import { DocumentationLinksService } from '../documentation_links'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks'; import { coreMock, scopedHistoryMock } from '../../../../../../../src/core/public/mocks';
import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock';
@ -23,6 +23,24 @@ describe('RoleMappingsGridPage', () => {
let history: ScopedHistory; let history: ScopedHistory;
let coreStart: CoreStart; let coreStart: CoreStart;
const renderView = (
roleMappingsAPI: ReturnType<typeof roleMappingsAPIClientMock.create>,
rolesAPI: ReturnType<typeof rolesAPIClientMock.create> = rolesAPIClientMock.create()
) => {
return mountWithIntl(
<KibanaContextProvider services={coreStart}>
<RoleMappingsGridPage
rolesAPIClient={rolesAPI}
roleMappingsAPI={roleMappingsAPI}
notifications={coreStart.notifications}
docLinks={coreStart.docLinks}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
</KibanaContextProvider>
);
};
beforeEach(() => { beforeEach(() => {
history = scopedHistoryMock.create(); history = scopedHistoryMock.create();
coreStart = coreMock.createStart(); coreStart = coreMock.createStart();
@ -36,17 +54,7 @@ describe('RoleMappingsGridPage', () => {
hasCompatibleRealms: true, hasCompatibleRealms: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(SectionLoading)).toHaveLength(1);
expect(wrapper.find(EmptyPrompt)).toHaveLength(0); expect(wrapper.find(EmptyPrompt)).toHaveLength(0);
@ -65,17 +73,7 @@ describe('RoleMappingsGridPage', () => {
hasCompatibleRealms: true, hasCompatibleRealms: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(SectionLoading)).toHaveLength(1);
expect(wrapper.find(PermissionDenied)).toHaveLength(0); expect(wrapper.find(PermissionDenied)).toHaveLength(0);
@ -102,17 +100,7 @@ describe('RoleMappingsGridPage', () => {
hasCompatibleRealms: false, hasCompatibleRealms: false,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(SectionLoading)).toHaveLength(1);
expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
@ -138,17 +126,7 @@ describe('RoleMappingsGridPage', () => {
hasCompatibleRealms: true, hasCompatibleRealms: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -172,17 +150,7 @@ describe('RoleMappingsGridPage', () => {
hasCompatibleRealms: true, hasCompatibleRealms: true,
}); });
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -212,17 +180,7 @@ describe('RoleMappingsGridPage', () => {
}, },
]); ]);
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={rolesAPIClientMock.create()}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();
@ -275,17 +233,7 @@ describe('RoleMappingsGridPage', () => {
}, },
]); ]);
const { docLinks, notifications } = coreMock.createStart(); const wrapper = renderView(roleMappingsAPI, roleAPIClient);
const wrapper = mountWithIntl(
<RoleMappingsGridPage
rolesAPIClient={roleAPIClient}
roleMappingsAPI={roleMappingsAPI}
notifications={notifications}
docLinks={new DocumentationLinksService(docLinks)}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
await nextTick(); await nextTick();
wrapper.update(); wrapper.update();

View file

@ -25,7 +25,12 @@ import {
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import { NotificationsStart, ApplicationStart, ScopedHistory } from 'src/core/public'; import type {
NotificationsStart,
ApplicationStart,
DocLinksStart,
ScopedHistory,
} from 'src/core/public';
import { RoleMapping, Role } from '../../../../common/model'; import { RoleMapping, Role } from '../../../../common/model';
import { EmptyPrompt } from './empty_prompt'; import { EmptyPrompt } from './empty_prompt';
import { import {
@ -35,7 +40,6 @@ import {
SectionLoading, SectionLoading,
} from '../components'; } from '../components';
import { EDIT_ROLE_MAPPING_PATH, getEditRoleMappingHref } from '../../management_urls'; import { EDIT_ROLE_MAPPING_PATH, getEditRoleMappingHref } from '../../management_urls';
import { DocumentationLinksService } from '../documentation_links';
import { RoleMappingsAPIClient } from '../role_mappings_api_client'; import { RoleMappingsAPIClient } from '../role_mappings_api_client';
import { RoleTableDisplay } from '../../role_table_display'; import { RoleTableDisplay } from '../../role_table_display';
import { RolesAPIClient } from '../../roles'; import { RolesAPIClient } from '../../roles';
@ -46,7 +50,7 @@ interface Props {
rolesAPIClient: PublicMethodsOf<RolesAPIClient>; rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
roleMappingsAPI: PublicMethodsOf<RoleMappingsAPIClient>; roleMappingsAPI: PublicMethodsOf<RoleMappingsAPIClient>;
notifications: NotificationsStart; notifications: NotificationsStart;
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
history: ScopedHistory; history: ScopedHistory;
navigateToApp: ApplicationStart['navigateToApp']; navigateToApp: ApplicationStart['navigateToApp'];
} }
@ -148,7 +152,7 @@ export class RoleMappingsGridPage extends Component<Props, State> {
values={{ values={{
learnMoreLink: ( learnMoreLink: (
<EuiLink <EuiLink
href={this.props.docLinks.getRoleMappingDocUrl()} href={this.props.docLinks.links.security.mappingRoles}
external={true} external={true}
target="_blank" target="_blank"
> >
@ -179,7 +183,7 @@ export class RoleMappingsGridPage extends Component<Props, State> {
<Fragment> <Fragment>
{!this.state.hasCompatibleRealms && ( {!this.state.hasCompatibleRealms && (
<> <>
<NoCompatibleRealms docLinks={this.props.docLinks} /> <NoCompatibleRealms />
<EuiSpacer /> <EuiSpacer />
</> </>
)} )}

View file

@ -5,11 +5,21 @@
*/ */
jest.mock('./role_mappings_grid', () => ({ jest.mock('./role_mappings_grid', () => ({
RoleMappingsGridPage: (props: any) => `Role Mappings Page: ${JSON.stringify(props)}`, RoleMappingsGridPage: (props: any) =>
// `docLinks` object is too big to include into test snapshot, so we just check its existence.
`Role Mappings Page: ${JSON.stringify({
...props,
docLinks: props.docLinks ? {} : undefined,
})}`,
})); }));
jest.mock('./edit_role_mapping', () => ({ jest.mock('./edit_role_mapping', () => ({
EditRoleMappingPage: (props: any) => `Role Mapping Edit Page: ${JSON.stringify(props)}`, EditRoleMappingPage: (props: any) =>
// `docLinks` object is too big to include into test snapshot, so we just check its existence.
`Role Mapping Edit Page: ${JSON.stringify({
...props,
docLinks: props.docLinks ? {} : undefined,
})}`,
})); }));
import { roleMappingsManagementApp } from './role_mappings_management_app'; import { roleMappingsManagementApp } from './role_mappings_management_app';
@ -54,7 +64,7 @@ describe('roleMappingsManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Role Mappings' }]); expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Role Mappings' }]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}} Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}}
</div> </div>
`); `);
@ -73,7 +83,7 @@ describe('roleMappingsManagementApp', () => {
]); ]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
</div> </div>
`); `);
@ -94,7 +104,7 @@ describe('roleMappingsManagementApp', () => {
]); ]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Mapping Edit Page: {"name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{"mappingRoles":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/mapping-roles.html","createRoleMapping":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html","createRoleMappingTemplates":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-api-put-role-mapping.html#_role_templates","roleMappingFieldRules":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/role-mapping-resources.html#mapping-roles-rule-field"},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}}} Role Mapping Edit Page: {"name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}}}
</div> </div>
`); `);

View file

@ -11,8 +11,8 @@ import { i18n } from '@kbn/i18n';
import { StartServicesAccessor } from 'src/core/public'; import { StartServicesAccessor } from 'src/core/public';
import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
import { PluginStartDependencies } from '../../plugin'; import { PluginStartDependencies } from '../../plugin';
import { DocumentationLinksService } from './documentation_links';
import { tryDecodeURIComponent } from '../url_utils'; import { tryDecodeURIComponent } from '../url_utils';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
interface CreateParams { interface CreateParams {
getStartServices: StartServicesAccessor<PluginStartDependencies>; getStartServices: StartServicesAccessor<PluginStartDependencies>;
@ -39,7 +39,7 @@ export const roleMappingsManagementApp = Object.freeze({
]; ];
const [ const [
[{ docLinks, http, notifications, i18n: i18nStart }], [core],
{ RoleMappingsGridPage }, { RoleMappingsGridPage },
{ EditRoleMappingPage }, { EditRoleMappingPage },
{ RoleMappingsAPIClient }, { RoleMappingsAPIClient },
@ -52,16 +52,15 @@ export const roleMappingsManagementApp = Object.freeze({
import('../roles'), import('../roles'),
]); ]);
const roleMappingsAPIClient = new RoleMappingsAPIClient(http); const roleMappingsAPIClient = new RoleMappingsAPIClient(core.http);
const dockLinksService = new DocumentationLinksService(docLinks);
const RoleMappingsGridPageWithBreadcrumbs = () => { const RoleMappingsGridPageWithBreadcrumbs = () => {
setBreadcrumbs(roleMappingsBreadcrumbs); setBreadcrumbs(roleMappingsBreadcrumbs);
return ( return (
<RoleMappingsGridPage <RoleMappingsGridPage
notifications={notifications} notifications={core.notifications}
rolesAPIClient={new RolesAPIClient(http)} rolesAPIClient={new RolesAPIClient(core.http)}
roleMappingsAPI={roleMappingsAPIClient} roleMappingsAPI={roleMappingsAPIClient}
docLinks={dockLinksService} docLinks={core.docLinks}
history={history} history={history}
navigateToApp={coreStart.application.navigateToApp} navigateToApp={coreStart.application.navigateToApp}
/> />
@ -90,27 +89,29 @@ export const roleMappingsManagementApp = Object.freeze({
<EditRoleMappingPage <EditRoleMappingPage
name={decodedName} name={decodedName}
roleMappingsAPI={roleMappingsAPIClient} roleMappingsAPI={roleMappingsAPIClient}
rolesAPIClient={new RolesAPIClient(http)} rolesAPIClient={new RolesAPIClient(core.http)}
notifications={notifications} notifications={core.notifications}
docLinks={dockLinksService} docLinks={core.docLinks}
history={history} history={history}
/> />
); );
}; };
render( render(
<i18nStart.Context> <KibanaContextProvider services={core}>
<Router history={history}> <core.i18n.Context>
<Switch> <Router history={history}>
<Route path={['/', '']} exact={true}> <Switch>
<RoleMappingsGridPageWithBreadcrumbs /> <Route path={['/', '']} exact={true}>
</Route> <RoleMappingsGridPageWithBreadcrumbs />
<Route path="/edit/:name?"> </Route>
<EditRoleMappingsPageWithBreadcrumbs /> <Route path="/edit/:name?">
</Route> <EditRoleMappingsPageWithBreadcrumbs />
</Switch> </Route>
</Router> </Switch>
</i18nStart.Context>, </Router>
</core.i18n.Context>
</KibanaContextProvider>,
element element
); );

View file

@ -1,31 +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 { DocLinksStart } from 'src/core/public';
export class DocumentationLinksService {
private readonly esClusterPrivileges: string;
private readonly esRunAsPrivilege: string;
private readonly esIndicesPrivileges: string;
constructor(docLinks: DocLinksStart) {
this.esClusterPrivileges = `${docLinks.links.security.clusterPrivileges}`;
this.esRunAsPrivilege = `${docLinks.links.security.runAsPrivilege}`;
this.esIndicesPrivileges = `${docLinks.links.security.indicesPrivileges}`;
}
public getESClusterPrivilegesDocUrl() {
return `${this.esClusterPrivileges}`;
}
public getESRunAsPrivilegesDocUrl() {
return `${this.esRunAsPrivilege}`;
}
public getESIndicesPrivilegesDocUrl() {
return `${this.esIndicesPrivileges}`;
}
}

View file

@ -11,7 +11,6 @@ import { mountWithIntl, nextTick } from '@kbn/test/jest';
import { Capabilities } from 'src/core/public'; import { Capabilities } from 'src/core/public';
import { KibanaFeature } from '../../../../../features/public'; import { KibanaFeature } from '../../../../../features/public';
import { Role } from '../../../../common/model'; import { Role } from '../../../../common/model';
import { DocumentationLinksService } from '../documentation_links';
import { EditRolePage } from './edit_role_page'; import { EditRolePage } from './edit_role_page';
import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section'; import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section';
@ -184,7 +183,7 @@ function getProps({
userAPIClient, userAPIClient,
getFeatures: () => Promise.resolve(buildFeatures()), getFeatures: () => Promise.resolve(buildFeatures()),
notifications, notifications,
docLinks: new DocumentationLinksService(docLinks), docLinks,
fatalErrors, fatalErrors,
uiCapabilities: buildUICapabilities(canManageSpaces), uiCapabilities: buildUICapabilities(canManageSpaces),
history: scopedHistoryMock.create(), history: scopedHistoryMock.create(),

View file

@ -38,7 +38,7 @@ import {
IHttpFetchError, IHttpFetchError,
NotificationsStart, NotificationsStart,
} from 'src/core/public'; } from 'src/core/public';
import { ScopedHistory } from 'kibana/public'; import type { DocLinksStart, ScopedHistory } from 'kibana/public';
import { FeaturesPluginStart } from '../../../../../features/public'; import { FeaturesPluginStart } from '../../../../../features/public';
import { KibanaFeature } from '../../../../../features/common'; import { KibanaFeature } from '../../../../../features/common';
import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public'; import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public';
@ -61,7 +61,6 @@ import { ElasticsearchPrivileges, KibanaPrivilegesRegion } from './privileges';
import { ReservedRoleBadge } from './reserved_role_badge'; import { ReservedRoleBadge } from './reserved_role_badge';
import { SecurityLicense } from '../../../../common/licensing'; import { SecurityLicense } from '../../../../common/licensing';
import { UserAPIClient } from '../../users'; import { UserAPIClient } from '../../users';
import { DocumentationLinksService } from '../documentation_links';
import { IndicesAPIClient } from '../indices_api_client'; import { IndicesAPIClient } from '../indices_api_client';
import { RolesAPIClient } from '../roles_api_client'; import { RolesAPIClient } from '../roles_api_client';
import { PrivilegesAPIClient } from '../privileges_api_client'; import { PrivilegesAPIClient } from '../privileges_api_client';
@ -77,7 +76,7 @@ interface Props {
rolesAPIClient: PublicMethodsOf<RolesAPIClient>; rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
privilegesAPIClient: PublicMethodsOf<PrivilegesAPIClient>; privilegesAPIClient: PublicMethodsOf<PrivilegesAPIClient>;
getFeatures: FeaturesPluginStart['getFeatures']; getFeatures: FeaturesPluginStart['getFeatures'];
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
http: HttpStart; http: HttpStart;
license: SecurityLicense; license: SecurityLicense;
uiCapabilities: Capabilities; uiCapabilities: Capabilities;

View file

@ -6,7 +6,6 @@
import React from 'react'; import React from 'react';
import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest';
import { DocumentationLinksService } from '../../../documentation_links';
import { RoleValidator } from '../../validate_role'; import { RoleValidator } from '../../validate_role';
import { ClusterPrivileges } from './cluster_privileges'; import { ClusterPrivileges } from './cluster_privileges';
import { ElasticsearchPrivileges } from './elasticsearch_privileges'; import { ElasticsearchPrivileges } from './elasticsearch_privileges';
@ -45,7 +44,7 @@ function getProps() {
index: ['all', 'read', 'write', 'index'], index: ['all', 'read', 'write', 'index'],
}, },
indicesAPIClient: indicesAPIClientMock.create(), indicesAPIClient: indicesAPIClientMock.create(),
docLinks: new DocumentationLinksService(docLinks), docLinks,
license, license,
}; };
} }

View file

@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import type { DocLinksStart } from 'src/core/public';
import { Role, BuiltinESPrivileges } from '../../../../../../common/model'; import { Role, BuiltinESPrivileges } from '../../../../../../common/model';
import { SecurityLicense } from '../../../../../../common/licensing'; import { SecurityLicense } from '../../../../../../common/licensing';
import { IndicesAPIClient } from '../../../indices_api_client'; import { IndicesAPIClient } from '../../../indices_api_client';
@ -26,13 +27,12 @@ import { RoleValidator } from '../../validate_role';
import { CollapsiblePanel } from '../../collapsible_panel'; import { CollapsiblePanel } from '../../collapsible_panel';
import { ClusterPrivileges } from './cluster_privileges'; import { ClusterPrivileges } from './cluster_privileges';
import { IndexPrivileges } from './index_privileges'; import { IndexPrivileges } from './index_privileges';
import { DocumentationLinksService } from '../../../documentation_links';
interface Props { interface Props {
role: Role; role: Role;
editable: boolean; editable: boolean;
indicesAPIClient: PublicMethodsOf<IndicesAPIClient>; indicesAPIClient: PublicMethodsOf<IndicesAPIClient>;
docLinks: DocumentationLinksService; docLinks: DocLinksStart;
license: SecurityLicense; license: SecurityLicense;
onChange: (role: Role) => void; onChange: (role: Role) => void;
runAsUsers: string[]; runAsUsers: string[];
@ -90,7 +90,7 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
id="xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription" id="xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription"
defaultMessage="Manage the actions this role can perform against your cluster. " defaultMessage="Manage the actions this role can perform against your cluster. "
/> />
{this.learnMore(docLinks.getESClusterPrivilegesDocUrl())} {this.learnMore(docLinks.links.security.clusterPrivileges)}
</p> </p>
} }
> >
@ -120,7 +120,7 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
id="xpack.security.management.editRole.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription" id="xpack.security.management.editRole.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription"
defaultMessage="Allow requests to be submitted on the behalf of other users. " defaultMessage="Allow requests to be submitted on the behalf of other users. "
/> />
{this.learnMore(docLinks.getESRunAsPrivilegesDocUrl())} {this.learnMore(docLinks.links.security.runAsPrivilege)}
</p> </p>
} }
> >
@ -164,7 +164,7 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
id="xpack.security.management.editRole.elasticSearchPrivileges.controlAccessToClusterDataDescription" id="xpack.security.management.editRole.elasticSearchPrivileges.controlAccessToClusterDataDescription"
defaultMessage="Control access to the data in your cluster. " defaultMessage="Control access to the data in your cluster. "
/> />
{this.learnMore(docLinks.getESIndicesPrivilegesDocUrl())} {this.learnMore(docLinks.links.security.indicesPrivileges)}
</p> </p>
</EuiText> </EuiText>

View file

@ -11,7 +11,9 @@ jest.mock('./roles_grid', () => ({
})); }));
jest.mock('./edit_role', () => ({ jest.mock('./edit_role', () => ({
EditRolePage: (props: any) => `Role Edit Page: ${JSON.stringify(props)}`, // `docLinks` object is too big to include into test snapshot, so we just check its existence.
EditRolePage: (props: any) =>
`Role Edit Page: ${JSON.stringify({ ...props, docLinks: props.docLinks ? {} : undefined })}`,
})); }));
import { rolesManagementApp } from './roles_management_app'; import { rolesManagementApp } from './roles_management_app';
@ -87,7 +89,7 @@ describe('rolesManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]); expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}} Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
</div> </div>
`); `);
@ -108,7 +110,7 @@ describe('rolesManagementApp', () => {
]); ]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}} Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}}
</div> </div>
`); `);
@ -126,7 +128,7 @@ describe('rolesManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]); expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: 'Create' }]);
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esClusterPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-cluster","esRunAsPrivilege":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#_run_as_privilege","esIndicesPrivileges":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-privileges.html#privileges-list-indices"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}} Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}}
</div> </div>
`); `);

View file

@ -12,7 +12,6 @@ import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public';
import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
import { SecurityLicense } from '../../../common/licensing'; import { SecurityLicense } from '../../../common/licensing';
import { PluginStartDependencies } from '../../plugin'; import { PluginStartDependencies } from '../../plugin';
import { DocumentationLinksService } from './documentation_links';
import { tryDecodeURIComponent } from '../url_utils'; import { tryDecodeURIComponent } from '../url_utils';
interface CreateParams { interface CreateParams {
@ -97,7 +96,7 @@ export const rolesManagementApp = Object.freeze({
notifications={notifications} notifications={notifications}
fatalErrors={fatalErrors} fatalErrors={fatalErrors}
license={license} license={license}
docLinks={new DocumentationLinksService(docLinks)} docLinks={docLinks}
uiCapabilities={application.capabilities} uiCapabilities={application.capabilities}
indexPatterns={data.indexPatterns} indexPatterns={data.indexPatterns}
history={history} history={history}

View file

@ -7,7 +7,7 @@ import React, { useState } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
import { render, unmountComponentAtNode } from 'react-dom'; import { render, unmountComponentAtNode } from 'react-dom';
import { MountPoint } from 'kibana/public'; import type { DocLinksStart, MountPoint } from 'kibana/public';
import { import {
EuiCheckbox, EuiCheckbox,
EuiText, EuiText,
@ -16,7 +16,6 @@ import {
EuiFlexItem, EuiFlexItem,
EuiButton, EuiButton,
} from '@elastic/eui'; } from '@elastic/eui';
import { DocumentationLinksService } from '../documentation_links';
export const insecureClusterAlertTitle = i18n.translate( export const insecureClusterAlertTitle = i18n.translate(
'xpack.security.checkup.insecureClusterTitle', 'xpack.security.checkup.insecureClusterTitle',
@ -24,12 +23,15 @@ export const insecureClusterAlertTitle = i18n.translate(
); );
export const insecureClusterAlertText = ( export const insecureClusterAlertText = (
getDocLinksService: () => DocumentationLinksService, getDocLinks: () => DocLinksStart,
onDismiss: (persist: boolean) => void onDismiss: (persist: boolean) => void
) => ) =>
((e) => { ((e) => {
const AlertText = () => { const AlertText = () => {
const [persist, setPersist] = useState(false); const [persist, setPersist] = useState(false);
const enableSecurityDocLink = `${
getDocLinks().links.security.elasticsearchEnableSecurity
}?blade=kibanasecuritymessage`;
return ( return (
<I18nProvider> <I18nProvider>
@ -56,7 +58,7 @@ export const insecureClusterAlertText = (
size="s" size="s"
color="primary" color="primary"
fill fill
href={getDocLinksService().getEnableSecurityDocUrl()} href={enableSecurityDocLink}
target="_blank" target="_blank"
data-test-subj="learnMoreButton" data-test-subj="learnMoreButton"
> >

View file

@ -1,19 +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 { DocLinksStart } from 'src/core/public';
export class DocumentationLinksService {
private readonly esEnableSecurity: string;
constructor(docLinks: DocLinksStart) {
this.esEnableSecurity = `${docLinks.links.security.elasticsearchEnableSecurity}`;
}
public getEnableSecurityDocUrl() {
return `${this.esEnableSecurity}?blade=kibanasecuritymessage`;
}
}

View file

@ -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 { DocLinksStart } from 'kibana/public'; import type { DocLinksStart } from 'kibana/public';
import { import {
SecurityOssPluginSetup, SecurityOssPluginSetup,
SecurityOssPluginStart, SecurityOssPluginStart,
} from '../../../../../src/plugins/security_oss/public'; } from '../../../../../src/plugins/security_oss/public';
import { insecureClusterAlertTitle, insecureClusterAlertText } from './components'; import { insecureClusterAlertTitle, insecureClusterAlertText } from './components';
import { DocumentationLinksService } from './documentation_links';
interface SetupDeps { interface SetupDeps {
securityOssSetup: SecurityOssPluginSetup; securityOssSetup: SecurityOssPluginSetup;
@ -25,13 +24,13 @@ interface StartDeps {
export class SecurityCheckupService { export class SecurityCheckupService {
private securityOssStart?: SecurityOssPluginStart; private securityOssStart?: SecurityOssPluginStart;
private docLinksService?: DocumentationLinksService; private docLinks?: DocLinksStart;
public setup({ securityOssSetup }: SetupDeps) { public setup({ securityOssSetup }: SetupDeps) {
securityOssSetup.insecureCluster.setAlertTitle(insecureClusterAlertTitle); securityOssSetup.insecureCluster.setAlertTitle(insecureClusterAlertTitle);
securityOssSetup.insecureCluster.setAlertText( securityOssSetup.insecureCluster.setAlertText(
insecureClusterAlertText( insecureClusterAlertText(
() => this.docLinksService!, () => this.docLinks!,
(persist: boolean) => this.onDismiss(persist) (persist: boolean) => this.onDismiss(persist)
) )
); );
@ -39,7 +38,7 @@ export class SecurityCheckupService {
public start({ securityOssStart, docLinks }: StartDeps) { public start({ securityOssStart, docLinks }: StartDeps) {
this.securityOssStart = securityOssStart; this.securityOssStart = securityOssStart;
this.docLinksService = new DocumentationLinksService(docLinks); this.docLinks = docLinks;
} }
private onDismiss(persist: boolean) { private onDismiss(persist: boolean) {