Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3a0c9b2
Revert "Temporarily remove Layout from App Search plugin for 7.10 rel…
constancecchen Oct 6, 2020
53ace58
Added a Credentials route and link
JasonStoltz Oct 1, 2020
6c352b8
Added Credentials Page
JasonStoltz Oct 1, 2020
9434cc8
Added Credentials List
JasonStoltz Oct 1, 2020
09e7d93
Merge branch 'master' into JasonStoltz/credentials-view
JasonStoltz Oct 7, 2020
c010b8a
Implement feedback items:
JasonStoltz Oct 7, 2020
e659382
Remove unused imports
JasonStoltz Oct 7, 2020
2c9a99a
PR Feedback
JasonStoltz Oct 7, 2020
91c4ff8
Add reusable shallow unmount helper
cee-chen Oct 6, 2020
714da22
Simplify Kea test mocks
cee-chen Oct 6, 2020
4917cbb
Switch to ApiTokenTypes
JasonStoltz Oct 7, 2020
280b2f7
Apply suggestions from code review
JasonStoltz Oct 7, 2020
30c52a7
Use ApiTokenTypes for IApiToken
JasonStoltz Oct 7, 2020
343290e
PR Feedback
JasonStoltz Oct 7, 2020
9e6b0c4
We can lose these CSS classes...
JasonStoltz Oct 7, 2020
564737a
I think we can rewrite this return flow
JasonStoltz Oct 7, 2020
7caf342
Missed a ApiTokenTypes issue
JasonStoltz Oct 7, 2020
c52c139
Do we want to write a test file for this?
JasonStoltz Oct 7, 2020
593a3a2
PR Feedback
JasonStoltz Oct 7, 2020
53865ce
Super exciting proposal here
JasonStoltz Oct 7, 2020
5d0ab10
Feedback
JasonStoltz Oct 7, 2020
b82eb10
Update x-pack/plugins/enterprise_search/public/applications/__mocks__…
JasonStoltz Oct 7, 2020
f653187
Apply suggestions from code review
JasonStoltz Oct 7, 2020
a30f8d5
nit - may as well keep EUI imports together
JasonStoltz Oct 7, 2020
49570d4
Just an idea / me going all #microperf here
JasonStoltz Oct 7, 2020
f41a356
Remove shallow_usememo
JasonStoltz Oct 8, 2020
d2e21d7
Apply suggestions from code review
JasonStoltz Oct 8, 2020
a0ff646
More feedback
JasonStoltz Oct 8, 2020
bfd75d8
mockKea simplifying
cee-chen Oct 8, 2020
473c1d2
Merge branch 'master' into JasonStoltz/credentials-view
JasonStoltz Oct 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,38 @@ export const ADMIN = 'admin';
export const PRIVATE = 'private';
export const SEARCH = 'search';

export const TOKEN_TYPE_DESCRIPTION = {
export const SEARCH_DISPLAY = i18n.translate(
'xpack.enterpriseSearch.appSearch.tokens.permissions.display.search',
{
defaultMessage: 'search',
}
);
export const ALL = i18n.translate(
'xpack.enterpriseSearch.appSearch.tokens.permissions.display.all',
{
defaultMessage: 'all',
}
);
export const READ_WRITE = i18n.translate(
'xpack.enterpriseSearch.appSearch.tokens.permissions.display.readwrite',
{
defaultMessage: 'read/write',
}
);
export const READ_ONLY = i18n.translate(
'xpack.enterpriseSearch.appSearch.tokens.permissions.display.readonly',
{
defaultMessage: 'read-only',
}
);
export const WRITE_ONLY = i18n.translate(
'xpack.enterpriseSearch.appSearch.tokens.permissions.display.writeonly',
{
defaultMessage: 'write-only',
}
);

export const TOKEN_TYPE_DESCRIPTION: { [key: string]: string } = {
[SEARCH]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.search.description', {
defaultMessage: 'Public Search Keys are used for search endpoints only.',
}),
Expand All @@ -22,7 +53,7 @@ export const TOKEN_TYPE_DESCRIPTION = {
}),
};

export const TOKEN_TYPE_DISPLAY_NAMES = {
export const TOKEN_TYPE_DISPLAY_NAMES: { [key: string]: string } = {
[SEARCH]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.search.name', {
defaultMessage: 'Public Search Key',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 '../../../__mocks__/kea.mock';
import '../../../__mocks__/shallow_useeffect.mock';

import React, { useEffect } from 'react';
import { shallow } from 'enzyme';
import { useValues, useActions } from 'kea';

import { Credentials } from './credentials';
import { EuiCopy, EuiPageContentBody } from '@elastic/eui';

import { externalUrl } from '../../../shared/enterprise_search_url';

const getUseEffectUnmountHandler = () => (useEffect as jest.Mock).mock.calls[0][0]();

describe('Credentials', () => {
const mockKea = ({ values = {}, actions = {} }) => {
const mergedValues = {
dataLoading: false,
...values,
};

const mergedActions = {
initializeCredentialsData: jest.fn,
...actions,
};

(useValues as jest.Mock).mockImplementationOnce(() => mergedValues);
(useActions as jest.Mock).mockImplementationOnce(() => mergedActions);
};

beforeEach(() => {
jest.clearAllMocks();
});

it('renders', () => {
mockKea({});
const wrapper = shallow(<Credentials />);
expect(wrapper.find(EuiPageContentBody)).toHaveLength(1);
});

it('initializes data on mount', () => {
const initializeCredentialsData = jest.fn();
mockKea({ actions: { initializeCredentialsData } });
shallow(<Credentials />);
expect(initializeCredentialsData).toHaveBeenCalledTimes(1);
});

it('calls resetCredentials on unmount', () => {
const resetCredentials = jest.fn();
mockKea({ actions: { resetCredentials } });
shallow(<Credentials />);
const unmountHandler = getUseEffectUnmountHandler();
unmountHandler();
expect(resetCredentials).toHaveBeenCalledTimes(1);
});

it('renders nothing if data is still loading', () => {
mockKea({ values: { dataLoading: true } });
const wrapper = shallow(<Credentials />);
expect(wrapper.find(EuiPageContentBody)).toHaveLength(0);
});

it('renders the API endpoint and a button to copy it', () => {
externalUrl.enterpriseSearchUrl = 'http://localhost:3002';
mockKea({});
const copyMock = jest.fn();
const wrapper = shallow(<Credentials />);
const copyEl = shallow(wrapper.find(EuiCopy).props().children(copyMock));
expect(copyEl.find('EuiButtonIcon').props().onClick).toEqual(copyMock);
expect(copyEl.find('span').text()).toEqual('http://localhost:3002');
});

it('will show the Crendentials Flyout when the Create API Key button is pressed', () => {
const showCredentialsForm = jest.fn();
mockKea({ actions: { showCredentialsForm } });
const wrapper = shallow(<Credentials />);
const button: any = wrapper.find('[data-test-subj="CreateAPIKeyButton"]');
button.props().onClick();
expect(showCredentialsForm).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useEffect } from 'react';
import { useActions, useValues } from 'kea';

import {
EuiPageHeader,
EuiPageHeaderSection,
EuiTitle,
EuiPageContentBody,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiCopy,
EuiButtonIcon,
EuiSpacer,
EuiButton,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import {
CredentialsLogic,
ICredentialsLogicActions,
ICredentialsLogicValues,
} from './credentials_logic';
import { externalUrl } from '../../../shared/enterprise_search_url/external_url';
import { CredentialsList } from './credentials_list';

export const Credentials: React.FC = () => {
const { initializeCredentialsData, resetCredentials, showCredentialsForm } = useActions(
CredentialsLogic
) as ICredentialsLogicActions;

const { dataLoading } = useValues(CredentialsLogic) as ICredentialsLogicValues;

useEffect(() => {
initializeCredentialsData();
return () => {
resetCredentials();
};
}, []);

// TODO
// if (dataLoading) { return <Loading /> }
if (dataLoading) {
return null;
}
return (
<>
<SetPageChrome
trail={[
i18n.translate('xpack.enterpriseSearch.appSearch.credentials.title', {
defaultMessage: 'Credentials',
}),
]}
/>
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>
{i18n.translate('xpack.enterpriseSearch.appSearch.credentials.title', {
defaultMessage: 'Credentials',
})}
</h1>
</EuiTitle>
</EuiPageHeaderSection>
</EuiPageHeader>
<EuiPageContentBody>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel style={{ textAlign: 'center' }}>
<EuiTitle size="s">
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.credentials.apiEndpoint', {
defaultMessage: 'Endpoint',
})}
</h2>
</EuiTitle>
<EuiCopy
textToCopy={externalUrl.enterpriseSearchUrl}
afterMessage={i18n.translate(
'xpack.enterpriseSearch.appSearch.credentials.copied',
{
defaultMessage: 'Copied',
}
)}
>
{(copy) => (
<div>
<EuiButtonIcon
onClick={copy}
iconType="copyClipboard"
aria-label={i18n.translate(
'xpack.enterpriseSearch.appSearch.credentials.copyApiEndpoint',
{
defaultMessage: 'Copy API Endpoint to clipboard.',
}
)}
/>
<span>{externalUrl.enterpriseSearchUrl}</span>
</div>
)}
</EuiCopy>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xxl" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="m">
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.credentials.apiKeys', {
defaultMessage: 'API Keys',
})}
</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
color="primary"
data-test-subj="CreateAPIKeyButton"
fill={true}
onClick={() => showCredentialsForm()}
>
{i18n.translate('xpack.enterpriseSearch.appSearch.credentials.createKey', {
defaultMessage: 'Create a key',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel>
<CredentialsList />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</>
);
};
Loading