-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[App Search] New log retention components + major i18n refactors (#88237
) * Add remaining log retention components - Will shortly be used by Analytics view * [i18n] Change log retention date from moment to react-intl FormattedDate - this correctly localizes the dates when Kibana is in different languages, e.g. zh-CN, ja-JP * Refactor log_retention/messaging - Convert to all JSX/React vs plain strings (makes it easier to deal with FormattedDate/FormattedMessages) - Consolidate to single component that takes various log types (e.g. analytics, api) - This is important for adding future switches & toggles - there is *significantly* less complex strings to localize this way (2 short strings vs 5+ long sentences) * Update existing instances to use new LogRetentionMessage * Update translation strings - to account for new i18n logs type values & IDs * Attempt to fix test timezone shenanigans * [PR feedback] Types * [PR feedback] i18n pluralization * Update LogRetentionTooltip and LogRetentionCallout to manage fetching log retention + change ILM to LogRetention per PR feedback * Update LogRetentionLogic to prevent duplicate fetches - e.g. if both LogRetentionTooltip and LogRetentionCallout are on the same page (which they will be)
- Loading branch information
Constance
authored
Jan 15, 2021
1 parent
0325014
commit ca42e9a
Showing
21 changed files
with
695 additions
and
516 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
...rprise_search/public/applications/app_search/components/log_retention/components/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export { LogRetentionCallout } from './log_retention_callout'; | ||
export { LogRetentionTooltip } from './log_retention_tooltip'; |
101 changes: 101 additions & 0 deletions
101
...pplications/app_search/components/log_retention/components/log_retention_callout.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* 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__/shallow_useeffect.mock'; | ||
import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock'; | ||
import { mountWithIntl } from '../../../../__mocks__'; | ||
|
||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import { EuiCallOut, EuiLink } from '@elastic/eui'; | ||
|
||
import { LogRetentionOptions } from '../'; | ||
import { LogRetentionCallout } from './'; | ||
|
||
describe('LogRetentionCallout', () => { | ||
const actions = { fetchLogRetention: jest.fn() }; | ||
const values = { myRole: { canManageLogSettings: true } }; | ||
const DISABLED = { | ||
disabledAt: '01 Jan 1970 12:00:00 +0000', | ||
enabled: false, | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
setMockActions(actions); | ||
}); | ||
|
||
it('renders an analytics callout', () => { | ||
setMockValues({ ...values, logRetention: { analytics: DISABLED } }); | ||
const wrapper = mountWithIntl(<LogRetentionCallout type={LogRetentionOptions.Analytics} />); | ||
|
||
expect(wrapper.find(EuiCallOut)).toHaveLength(1); | ||
expect(wrapper.find('.euiCallOutHeader__title').text()).toEqual( | ||
'Analytics have been disabled since January 1, 1970.' | ||
); | ||
expect(wrapper.find(EuiLink)).toHaveLength(1); | ||
expect(wrapper.find('p').text()).toEqual('To manage analytics & logging, visit your settings.'); | ||
}); | ||
|
||
it('renders an API callout', () => { | ||
setMockValues({ ...values, logRetention: { api: DISABLED } }); | ||
const wrapper = mountWithIntl(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.find(EuiCallOut)).toHaveLength(1); | ||
expect(wrapper.find('.euiCallOutHeader__title').text()).toEqual( | ||
'API Logs have been disabled since January 1, 1970.' | ||
); | ||
expect(wrapper.find(EuiLink)).toHaveLength(1); | ||
expect(wrapper.find('p').text()).toEqual('To manage analytics & logging, visit your settings.'); | ||
}); | ||
|
||
it('renders a generic title if no disabled date is present', () => { | ||
setMockValues({ ...values, logRetention: { api: { enabled: false, disabledAt: null } } }); | ||
const wrapper = mountWithIntl(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.find(EuiCallOut)).toHaveLength(1); | ||
expect(wrapper.find('.euiCallOutHeader__title').text()).toEqual('API Logs have been disabled.'); | ||
}); | ||
|
||
it('does not render a settings link if the user cannot manage settings', () => { | ||
setMockValues({ myRole: { canManageLogSettings: false }, logRetention: { api: DISABLED } }); | ||
const wrapper = mountWithIntl(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.find(EuiCallOut)).toHaveLength(1); | ||
expect(wrapper.find(EuiLink)).toHaveLength(0); | ||
expect(wrapper.find('p')).toHaveLength(0); | ||
}); | ||
|
||
it('does not render if log retention is enabled', () => { | ||
setMockValues({ ...values, logRetention: { api: { enabled: true } } }); | ||
const wrapper = shallow(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.isEmptyRender()).toBe(true); | ||
}); | ||
|
||
it('does not render if log retention is not available', () => { | ||
setMockValues({ ...values, logRetention: null }); | ||
const wrapper = shallow(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.isEmptyRender()).toBe(true); | ||
}); | ||
|
||
describe('on mount', () => { | ||
it('fetches log retention data when not already loaded', () => { | ||
setMockValues({ ...values, logRetention: null }); | ||
shallow(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(actions.fetchLogRetention).toHaveBeenCalled(); | ||
}); | ||
|
||
it('does not fetch log retention data if it has already been loaded', () => { | ||
setMockValues({ ...values, logRetention: {} }); | ||
shallow(<LogRetentionCallout type={LogRetentionOptions.API} />); | ||
|
||
expect(actions.fetchLogRetention).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
95 changes: 95 additions & 0 deletions
95
...lic/applications/app_search/components/log_retention/components/log_retention_callout.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* 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 { useValues, useActions } from 'kea'; | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
import { FormattedMessage } from '@kbn/i18n/react'; | ||
import { EuiCallOut, EuiSpacer } from '@elastic/eui'; | ||
|
||
import { EuiLinkTo } from '../../../../shared/react_router_helpers'; | ||
|
||
import { AppLogic } from '../../../app_logic'; | ||
import { SETTINGS_PATH } from '../../../routes'; | ||
import { ANALYTICS_TITLE } from '../../analytics'; | ||
import { API_LOGS_TITLE } from '../../api_logs'; | ||
|
||
import { LogRetentionLogic, LogRetentionOptions, renderLogRetentionDate } from '../'; | ||
|
||
const TITLE_MAP = { | ||
[LogRetentionOptions.Analytics]: ANALYTICS_TITLE, | ||
[LogRetentionOptions.API]: API_LOGS_TITLE, | ||
}; | ||
|
||
interface Props { | ||
type: LogRetentionOptions; | ||
} | ||
export const LogRetentionCallout: React.FC<Props> = ({ type }) => { | ||
const { fetchLogRetention } = useActions(LogRetentionLogic); | ||
const { logRetention } = useValues(LogRetentionLogic); | ||
const { | ||
myRole: { canManageLogSettings }, | ||
} = useValues(AppLogic); | ||
|
||
const hasLogRetention = logRetention !== null; | ||
|
||
useEffect(() => { | ||
if (!hasLogRetention) fetchLogRetention(); | ||
}, []); | ||
|
||
const logRetentionSettings = logRetention?.[type]; | ||
const title = TITLE_MAP[type]; | ||
const hasLogRetentionDisabled = hasLogRetention && !logRetentionSettings?.enabled; | ||
|
||
return hasLogRetentionDisabled ? ( | ||
<> | ||
<EuiCallOut | ||
iconType="alert" | ||
color="primary" | ||
title={ | ||
logRetentionSettings?.disabledAt ? ( | ||
<FormattedMessage | ||
id="xpack.enterpriseSearch.appSearch.logRetention.callout.disabledSinceTitle" | ||
defaultMessage="{logsTitle} have been disabled since {disabledDate}." | ||
values={{ | ||
logsTitle: title, | ||
disabledDate: renderLogRetentionDate(logRetentionSettings.disabledAt), | ||
}} | ||
/> | ||
) : ( | ||
i18n.translate('xpack.enterpriseSearch.appSearch.logRetention.callout.disabledTitle', { | ||
defaultMessage: '{logsTitle} have been disabled.', | ||
values: { | ||
logsTitle: title, | ||
}, | ||
}) | ||
) | ||
} | ||
> | ||
{canManageLogSettings && ( | ||
<p> | ||
<FormattedMessage | ||
id="xpack.enterpriseSearch.appSearch.logRetention.callout.description.manageSettingsDetail" | ||
defaultMessage="To manage analytics & logging, {visitSettingsLink}." | ||
values={{ | ||
visitSettingsLink: ( | ||
<EuiLinkTo to={SETTINGS_PATH}> | ||
{i18n.translate( | ||
'xpack.enterpriseSearch.appSearch.logRetention.callout.description.manageSettingsLinkText', | ||
{ defaultMessage: 'visit your settings' } | ||
)} | ||
</EuiLinkTo> | ||
), | ||
}} | ||
/> | ||
</p> | ||
)} | ||
</EuiCallOut> | ||
<EuiSpacer /> | ||
</> | ||
) : null; | ||
}; |
73 changes: 73 additions & 0 deletions
73
...pplications/app_search/components/log_retention/components/log_retention_tooltip.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* 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__/shallow_useeffect.mock'; | ||
import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock'; | ||
|
||
import React from 'react'; | ||
import { shallow, mount } from 'enzyme'; | ||
import { EuiIconTip } from '@elastic/eui'; | ||
|
||
import { LogRetentionOptions, LogRetentionMessage } from '../'; | ||
import { LogRetentionTooltip } from './'; | ||
|
||
describe('LogRetentionTooltip', () => { | ||
const values = { logRetention: {} }; | ||
const actions = { fetchLogRetention: jest.fn() }; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
setMockValues(values); | ||
setMockActions(actions); | ||
}); | ||
|
||
it('renders an analytics tooltip', () => { | ||
const wrapper = shallow(<LogRetentionTooltip type={LogRetentionOptions.Analytics} />); | ||
const tooltipContent = wrapper.find(EuiIconTip).prop('content') as React.ReactElement; | ||
|
||
expect(tooltipContent.type).toEqual(LogRetentionMessage); | ||
expect(tooltipContent.props.type).toEqual('analytics'); | ||
}); | ||
|
||
it('renders an API tooltip', () => { | ||
const wrapper = shallow(<LogRetentionTooltip type={LogRetentionOptions.API} />); | ||
const tooltipContent = wrapper.find(EuiIconTip).prop('content') as React.ReactElement; | ||
|
||
expect(tooltipContent.type).toEqual(LogRetentionMessage); | ||
expect(tooltipContent.props.type).toEqual('api'); | ||
}); | ||
|
||
it('passes custom tooltip positions', () => { | ||
const wrapper = shallow(<LogRetentionTooltip type={LogRetentionOptions.API} />); | ||
expect(wrapper.find(EuiIconTip).prop('position')).toEqual('bottom'); | ||
|
||
wrapper.setProps({ position: 'right' }); | ||
expect(wrapper.find(EuiIconTip).prop('position')).toEqual('right'); | ||
}); | ||
|
||
it('does not render if log retention is not available', () => { | ||
setMockValues({ logRetention: null }); | ||
const wrapper = mount(<LogRetentionTooltip type={LogRetentionOptions.API} />); | ||
|
||
expect(wrapper.isEmptyRender()).toBe(true); | ||
}); | ||
|
||
describe('on mount', () => { | ||
it('fetches log retention data when not already loaded', () => { | ||
setMockValues({ logRetention: null }); | ||
shallow(<LogRetentionTooltip type={LogRetentionOptions.Analytics} />); | ||
|
||
expect(actions.fetchLogRetention).toHaveBeenCalled(); | ||
}); | ||
|
||
it('does not fetch log retention data if it has already been loaded', () => { | ||
setMockValues({ logRetention: {} }); | ||
shallow(<LogRetentionTooltip type={LogRetentionOptions.Analytics} />); | ||
|
||
expect(actions.fetchLogRetention).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
41 changes: 41 additions & 0 deletions
41
...lic/applications/app_search/components/log_retention/components/log_retention_tooltip.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* 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 { useValues, useActions } from 'kea'; | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
import { EuiIconTip } from '@elastic/eui'; | ||
|
||
import { LogRetentionLogic, LogRetentionMessage, LogRetentionOptions } from '../'; | ||
|
||
interface Props { | ||
type: LogRetentionOptions; | ||
position?: 'top' | 'right' | 'bottom' | 'left'; | ||
} | ||
export const LogRetentionTooltip: React.FC<Props> = ({ type, position = 'bottom' }) => { | ||
const { fetchLogRetention } = useActions(LogRetentionLogic); | ||
const { logRetention } = useValues(LogRetentionLogic); | ||
|
||
const hasLogRetention = logRetention !== null; | ||
|
||
useEffect(() => { | ||
if (!hasLogRetention) fetchLogRetention(); | ||
}, []); | ||
|
||
return hasLogRetention ? ( | ||
<EuiIconTip | ||
aria-label={i18n.translate('xpack.enterpriseSearch.appSearch.logRetention.tooltip', { | ||
defaultMessage: 'Log retention info', | ||
})} | ||
size="l" | ||
type="iInCircle" | ||
color="primary" | ||
position={position} | ||
content={<LogRetentionMessage type={type} />} | ||
/> | ||
) : null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.