Skip to content

Commit

Permalink
[8.x] [Index Management] Fix globalMaxRetention display error in data…
Browse files Browse the repository at this point in the history
… retention modal (#198113) (#198534)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Index Management] Fix globalMaxRetention display error in data
retention modal
(#198113)](#198113)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Jusheng
Huang","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-31T13:52:48Z","message":"[Index
Management] Fix globalMaxRetention display error in data retention modal
(#198113)\n\n## Summary\r\n\r\nFixes #197314 \r\n\r\n**After
fixup:**\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/9fa66906-fa06-4e12-81d8-7bcdaaff3ff3)\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/a7b9ee72-4ce3-4d2b-8c14-483ea2028597)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<[email protected]>","sha":"1e7bb1ea37e7aa8b1ebb77f22fa34e2f1e3302a0","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Index
Management","Team:Kibana
Management","release_note:skip","💝community","v9.0.0","backport:prev-minor"],"title":"[Index
Management] Fix globalMaxRetention display error in data retention
modal","number":198113,"url":"https://github.com/elastic/kibana/pull/198113","mergeCommit":{"message":"[Index
Management] Fix globalMaxRetention display error in data retention modal
(#198113)\n\n## Summary\r\n\r\nFixes #197314 \r\n\r\n**After
fixup:**\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/9fa66906-fa06-4e12-81d8-7bcdaaff3ff3)\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/a7b9ee72-4ce3-4d2b-8c14-483ea2028597)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<[email protected]>","sha":"1e7bb1ea37e7aa8b1ebb77f22fa34e2f1e3302a0"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/198113","number":198113,"mergeCommit":{"message":"[Index
Management] Fix globalMaxRetention display error in data retention modal
(#198113)\n\n## Summary\r\n\r\nFixes #197314 \r\n\r\n**After
fixup:**\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/9fa66906-fa06-4e12-81d8-7bcdaaff3ff3)\r\n\r\n\r\n![image](https://github.com/user-attachments/assets/a7b9ee72-4ce3-4d2b-8c14-483ea2028597)\r\n\r\n---------\r\n\r\nCo-authored-by:
Elastic Machine
<[email protected]>","sha":"1e7bb1ea37e7aa8b1ebb77f22fa34e2f1e3302a0"}}]}]
BACKPORT-->

Co-authored-by: Jusheng Huang <[email protected]>
  • Loading branch information
kibanamachine and viajes7 authored Oct 31, 2024
1 parent cf0ab00 commit bc282cb
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { getLifecycleValue } from './data_streams';
import { deserializeGlobalMaxRetention, getLifecycleValue } from './data_streams';

describe('Data stream helpers', () => {
describe('getLifecycleValue', () => {
Expand Down Expand Up @@ -45,4 +45,36 @@ describe('Data stream helpers', () => {
).toBe('5 days');
});
});

describe('deserializeGlobalMaxRetention', () => {
it('if globalMaxRetention is undefined', () => {
expect(deserializeGlobalMaxRetention(undefined)).toEqual({});
});

it('split globalMaxRetention size and units', () => {
expect(deserializeGlobalMaxRetention('1000h')).toEqual({
size: '1000',
unit: 'h',
unitText: 'hours',
});
});

it('support all of the units that are accepted by es', () => {
expect(deserializeGlobalMaxRetention('1000ms')).toEqual({
size: '1000',
unit: 'ms',
unitText: 'milliseconds',
});
expect(deserializeGlobalMaxRetention('1000micros')).toEqual({
size: '1000',
unit: 'micros',
unitText: 'microseconds',
});
expect(deserializeGlobalMaxRetention('1000nanos')).toEqual({
size: '1000',
unit: 'nanos',
unitText: 'nanoseconds',
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,19 @@ export const isDSLWithILMIndices = (dataStream?: DataStream | null) => {

return;
};

export const deserializeGlobalMaxRetention = (globalMaxRetention?: string) => {
if (!globalMaxRetention) {
return {};
}

const { size, unit } = splitSizeAndUnits(globalMaxRetention);
const availableTimeUnits = [...timeUnits, ...extraTimeUnits];
const match = availableTimeUnits.find((timeUnit) => timeUnit.value === unit);

return {
size,
unit,
unitText: match?.text ?? unit,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { getIndexListUri } from '../../../../services/routing';
import { documentationService } from '../../../../services/documentation';
import { splitSizeAndUnits, DataStream } from '../../../../../../common';
import { timeUnits } from '../../../../constants/time_units';
import { isDSLWithILMIndices } from '../../../../lib/data_streams';
import { deserializeGlobalMaxRetention, isDSLWithILMIndices } from '../../../../lib/data_streams';
import { useAppContext } from '../../../../app_context';
import { UnitField } from '../../../../components/shared';
import { updateDataRetention } from '../../../../services/api';
Expand Down Expand Up @@ -214,6 +214,7 @@ export const EditDataRetentionModal: React.FunctionComponent<Props> = ({
const { history } = useAppContext();
const dslWithIlmIndices = isDSLWithILMIndices(dataStream);
const { size, unit } = splitSizeAndUnits(lifecycle?.data_retention as string);
const globalMaxRetention = deserializeGlobalMaxRetention(lifecycle?.globalMaxRetention);
const {
services: { notificationService },
config: { enableTogglingDataRetention, enableProjectLevelRetentionChecks },
Expand Down Expand Up @@ -331,8 +332,11 @@ export const EditDataRetentionModal: React.FunctionComponent<Props> = ({
<>
<FormattedMessage
id="xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.modalTitleText"
defaultMessage="Maximum data retention period is {maxRetention} days"
values={{ maxRetention: lifecycle?.globalMaxRetention.slice(0, -1) }}
defaultMessage="Maximum data retention period is {maxRetention} {unitText}"
values={{
maxRetention: globalMaxRetention.size,
unitText: globalMaxRetention.unitText,
}}
/>
<EuiSpacer />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ describe('isBiggerThanGlobalMaxRetention', () => {
});
});

it('should correctly compare retention in all of the units that are accepted by es', () => {
// 1000 milliseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(1, 's', '1000ms')).toBeUndefined();
expect(isBiggerThanGlobalMaxRetention(2, 's', '1000ms')).toEqual({
message: 'Maximum data retention period on this project is 1000 milliseconds.',
});

// 1000000 microseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(1, 's', '1000000micros')).toBeUndefined();
expect(isBiggerThanGlobalMaxRetention(2, 'm', '1000000micros')).toEqual({
message: 'Maximum data retention period on this project is 1000000 microseconds.',
});

// 1000000000 microseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(2, 's', '1000000000nanos'));
expect(isBiggerThanGlobalMaxRetention(2, 'h', '1000000000nanos')).toEqual({
message: 'Maximum data retention period on this project is 1000000000 nanoseconds.',
});
});

it('should throw an error for unknown time units', () => {
expect(() => isBiggerThanGlobalMaxRetention(10, 'x', '30d')).toThrow('Unknown unit: x');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,44 @@

import { i18n } from '@kbn/i18n';
import { splitSizeAndUnits } from '../../../../../../common';
import { deserializeGlobalMaxRetention } from '../../../../lib/data_streams';

const convertToMinutes = (value: string) => {
const convertToSeconds = (value: string) => {
const { size, unit } = splitSizeAndUnits(value);
const sizeNum = parseInt(size, 10);

switch (unit) {
case 'd':
// days to minutes
return sizeNum * 24 * 60;
// days to seconds
return sizeNum * 24 * 60 * 60;
case 'h':
// hours to minutes
return sizeNum * 60;
// hours to seconds
return sizeNum * 60 * 60;
case 'm':
// minutes to minutes
return sizeNum;
// minutes to seconds
return sizeNum * 60;
case 's':
// seconds to minutes (round up if any remainder)
return Math.ceil(sizeNum / 60);
// seconds to seconds
return sizeNum;
case 'ms':
// milliseconds to seconds
return sizeNum / 1000;
case 'micros':
// microseconds to seconds
return sizeNum / 1000 / 1000;
case 'nanos':
// nanoseconds to seconds
return sizeNum / 1000 / 1000 / 1000;
default:
throw new Error(`Unknown unit: ${unit}`);
}
};

const isRetentionBiggerThan = (valueA: string, valueB: string) => {
const minutesA = convertToMinutes(valueA);
const minutesB = convertToMinutes(valueB);
const secondsA = convertToSeconds(valueA);
const secondsB = convertToSeconds(valueB);

return minutesA > minutesB;
return secondsA > secondsB;
};

export const isBiggerThanGlobalMaxRetention = (
Expand All @@ -46,14 +56,19 @@ export const isBiggerThanGlobalMaxRetention = (
return undefined;
}

const { size, unitText } = deserializeGlobalMaxRetention(globalMaxRetention);
return isRetentionBiggerThan(`${retentionValue}${retentionTimeUnit}`, globalMaxRetention)
? {
message: i18n.translate(
'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldMaxError',
{
defaultMessage: 'Maximum data retention period on this project is {maxRetention} days.',
defaultMessage:
'Maximum data retention period on this project is {maxRetention} {unitText}.',
// Remove the unit from the globalMaxRetention value
values: { maxRetention: globalMaxRetention.slice(0, -1) },
values: {
maxRetention: size,
unitText,
},
}
),
}
Expand Down

0 comments on commit bc282cb

Please sign in to comment.