Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Frontend for Custom Result Index Query and Fix Issues #772

Merged
merged 3 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"babel-polyfill": "^6.26.0",
"eslint-plugin-no-unsanitized": "^3.0.2",
"eslint-plugin-prefer-object-spread": "^1.2.1",
"jest-canvas-mock": "^2.5.2",
"jest-canvas-mock": "^2.5.1",
"lint-staged": "^9.2.0",
"moment": "^2.24.0",
"redux-mock-store": "^1.5.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function CustomResultIndex(props: CustomResultIndexProps) {
<EuiFlexItem>
<EuiCallOut
data-test-subj="cannotEditResultIndexCallout"
title="You can't change the custom result index after creating the detector. You can manage the result index using the following three settings inside Anomaly Detection plugin or with the Index Management plugin."
title="You can't change the custom result index after creating the detector. You can manage the result index using the following three settings."
color="warning"
iconType="alert"
size="s"
Expand Down
28 changes: 17 additions & 11 deletions public/pages/DetectorDetail/containers/DetectorDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
getDetector,
stopHistoricalDetector,
} from '../../../redux/reducers/ad';
import { getIndices } from '../../../redux/reducers/opensearch';
import { getAliases, getIndices } from '../../../redux/reducers/opensearch';
import { getErrorMessage, Listener } from '../../../utils/utils';
import { darkModeEnabled } from '../../../utils/opensearchDashboardsUtils';
import { BREADCRUMBS, MDS_BREADCRUMBS } from '../../../utils/constants';
Expand All @@ -62,8 +62,8 @@ import {
prettifyErrorMessage,
} from '../../../../server/utils/helpers';
import { DETECTOR_STATE } from '../../../../server/utils/constants';
import { CatIndex } from '../../../../server/models/types';
import { containsIndex } from '../utils/helpers';
import { CatIndex, IndexAlias } from '../../../../server/models/types';
import { containsIndex, containsAlias } from '../utils/helpers';
import { DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public';
import {
getDataSourceManagementPlugin,
Expand Down Expand Up @@ -136,29 +136,34 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
const isCatIndicesRequesting = useSelector(
(state: AppState) => state.opensearch.requesting
) as boolean;
const visibleAliases = useSelector(
(state: AppState) => state.opensearch.aliases
) as IndexAlias[];

/*
Determine if the result index is missing based on several conditions:
- If the detector is still loading, the result index is not missing.
- If the result index retrieved from the detector is empty, it is not missing.
- If cat indices are being requested, the result index is not missing.
- If visible indices are empty, it is likely there is an issue retrieving existing indices.
- If visible indices/aliaes are empty, it is likely there is an issue retrieving existing indices.
To be safe, we'd rather not show the error message and consider the result index not missing.
- If the result index is not found in the visible indices, then it is missing.
*/
const resultIndexOrAlias = get(detector, 'resultIndex', '')
const isResultIndexMissing = isLoadingDetector
? false
: isEmpty(get(detector, 'resultIndex', ''))
? false
: isCatIndicesRequesting
? false
: isEmpty(visibleIndices)
: isEmpty(visibleIndices) || isEmpty(visibleAliases)
? false
: !containsIndex(get(detector, 'resultIndex', ''), visibleIndices);
: !containsIndex(resultIndexOrAlias, visibleIndices) && !containsAlias(resultIndexOrAlias, visibleAliases);

// debug message: prints visibleIndices if isResultIndexMissing is true
if (isResultIndexMissing) {
console.log(`isResultIndexMissing is true, visibleIndices: ${visibleIndices}, detector result index: ${get(detector, 'resultIndex', '')}`);
// The JSON.stringify method converts a JavaScript object or value to a JSON string. The optional null parameter is for the replacer function (not used here), and 2 specifies the indentation level for pretty-printing the JSON.
console.log(`isResultIndexMissing is true, visibleIndices: ${JSON.stringify(visibleIndices, null, 2)}, visibleAliases: ${JSON.stringify(visibleAliases, null, 2)}, detector result index: ${resultIndexOrAlias}`);
}

// String to set in the modal if the realtime detector and/or historical analysis
Expand Down Expand Up @@ -197,20 +202,21 @@ export const DetectorDetail = (props: DetectorDetailProps) => {
scroll(0, 0);
}, []);

// Getting all visible indices. Will re-fetch if changes to the detector (e.g.,
// Getting all visible indices & aliases. Will re-fetch if changes to the detector (e.g.,
// detector starts, result index recreated or user switches tabs to re-fetch detector)
useEffect(() => {
const getInitialIndices = async () => {
const getInitialIndicesAliases = async () => {
try {
await dispatch(getIndices('', dataSourceId));
await dispatch(getAliases('', dataSourceId));
amitgalitz marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
console.error(error);
core.notifications.toasts.addDanger('Error getting all indices');
core.notifications.toasts.addDanger('Error getting all indices or aliases');
}
};
// only need to check if indices exist after detector finishes loading
if (!isLoadingDetector) {
getInitialIndices();
getInitialIndicesAliases();
}
}, [detector]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('detector detail', () => {
expect(element).toBeNull();
});

test('the result index is not found in the visible indices', () => {
test('the result index is not found in the visible indices but alias is empty', () => {
const detectorInfo = {
detector: getRandomDetector(true, resultIndex),
hasError: false,
Expand All @@ -236,7 +236,7 @@ describe('detector detail', () => {
const element = screen.queryByTestId('missingResultIndexCallOut');

// Assert that the element is in the document
expect(element).not.toBeNull();
expect(element).toBeNull();
});

test('the result index is found in the visible indices', () => {
Expand Down Expand Up @@ -272,4 +272,81 @@ describe('detector detail', () => {
// Assert that the element is not in the document
expect(element).toBeNull();
});

test('the result index prefix is found in the visible aliaes', () => {
const detector = getRandomDetector(true, resultIndex);
const resultIndexFull = resultIndex + '-history-2024.06.05-1';

// Set up the mock implementation for useFetchDetectorInfo
(useFetchDetectorInfo as jest.Mock).mockImplementation(() => ({
detector: detector,
hasError: false,
isLoadingDetector: false,
errorMessage: undefined,
}));

const initialState = {
opensearch: {
indices: [
{ health: 'green', index: '.kibana_-962704462_v992471_1' },
{ health: 'green', index: resultIndexFull},
],
aliases : [
{index: '.opendistro-anomaly-results-history-2024.06.08-1', alias: '.opendistro-anomaly-results'},
{index: resultIndexFull, alias: resultIndex},
{index: '.kibana_1', alias: '.kibana'},
],
requesting: false,
},
ad: {
detectors: {},
},
alerting: {
monitors: {},
},
};

renderWithRouter(detectorId, initialState);
const element = screen.queryByTestId('missingResultIndexCallOut');

// Assert that the element is not in the document
expect(element).toBeNull();
});

test('the result index prefix is not found in both visible aliaes and indices', () => {
const detector = getRandomDetector(true, resultIndex);

// Set up the mock implementation for useFetchDetectorInfo
(useFetchDetectorInfo as jest.Mock).mockImplementation(() => ({
detector: detector,
hasError: false,
isLoadingDetector: false,
errorMessage: undefined,
}));

const initialState = {
opensearch: {
indices: [
{ health: 'green', index: '.kibana_-962704462_v992471_1' },
],
aliases : [
{index: '.opendistro-anomaly-results-history-2024.06.08-1', alias: '.opendistro-anomaly-results'},
{index: '.kibana_1', alias: '.kibana'},
],
requesting: false,
},
ad: {
detectors: {},
},
alerting: {
monitors: {},
},
};

renderWithRouter(detectorId, initialState);
const element = screen.queryByTestId('missingResultIndexCallOut');

// Assert that the element is not in the document
expect(element).not.toBeNull();
});
});
25 changes: 24 additions & 1 deletion public/pages/DetectorDetail/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { DETECTOR_STATE } from '../../../../server/utils/constants';
import { Detector } from '../../../models/interfaces';
import { EuiHealth } from '@elastic/eui';
import moment from 'moment';
import { CatIndex } from '../../../../server/models/types';
import { CatIndex, IndexAlias } from '../../../../server/models/types';

export const getInitFailureMessageAndActionItem = (error: string): object => {
const failureDetails = Object.values(DETECTOR_INIT_FAILURES);
Expand Down Expand Up @@ -142,6 +142,17 @@ export const getDetectorStateDetails = (
);
};

/**
* Checks if any of the given indices contain the specified index.
*
* This function iterates through an array of `CatIndex` objects and checks if the `index` property of any
* `CatIndex` object equals to the specified `index` string. It returns `true` if such an `index` is found,
* otherwise it returns `false`.
*
* @param index - The string to check against the `index` properties of the `CatIndex` objects.
* @param indices - An array of `CatIndex` objects to search through.
* @returns A boolean value indicating whether any `CatIndex` object's `index` property equals to the specified prefix.
*/
export const containsIndex = (index: string, indices: CatIndex[]) => {
let containsIndex = false;
if (!isEmpty(indices)) {
Expand All @@ -153,3 +164,15 @@ export const containsIndex = (index: string, indices: CatIndex[]) => {
}
return containsIndex;
};

export const containsAlias = (alias: string, aliases: IndexAlias[]) => {
let containsAlias = false;
if (!isEmpty(aliases)) {
aliases.forEach((catAlias: IndexAlias) => {
if (get(catAlias, 'alias', '') == alias) {
containsAlias = true;
}
});
}
return containsAlias;
};
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export const DetectorDefinitionFields = (
}
};

const minAge = get(props, 'detector.resultIndexMinAge', '-');
const minSize = get(props, 'detector.resultIndexMinSize', '-');
const ttl = get(props, 'detector.resultIndexTtl', '-');

return (
<ContentPanel
title="Detector settings"
Expand Down Expand Up @@ -220,19 +224,19 @@ export const DetectorDefinitionFields = (
<EuiFlexItem>
<ConfigCell
title="Custom result index min age"
description={get(props, 'detector.resultIndexMinAge', '-') + ' Days'}
description={minAge === '-' ? minAge : `${minAge} Days`}
/>
</EuiFlexItem>
<EuiFlexItem>
<ConfigCell
title="Custom result index min size"
description={get(props, 'detector.resultIndexMinSize', '-') + ' MB'}
description={minSize == '-' ? minSize : `${minSize} MB`}
/>
</EuiFlexItem>
<EuiFlexItem>
<ConfigCell
title="Custom result index TTL"
description={get(props, 'detector.resultIndexTtl', '-') + ' Days'}
description={ttl == '-' ? ttl : `${ttl} Days`}
/>
</EuiFlexItem>
</EuiFlexGrid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ exports[`<ReviewAndCreate /> spec renders the component, validation loading 1`]
<p
class="enabled"
>
- Days
-
</p>
</div>
</div>
Expand Down Expand Up @@ -458,7 +458,7 @@ exports[`<ReviewAndCreate /> spec renders the component, validation loading 1`]
<p
class="enabled"
>
- MB
-
</p>
</div>
</div>
Expand Down Expand Up @@ -492,7 +492,7 @@ exports[`<ReviewAndCreate /> spec renders the component, validation loading 1`]
<p
class="enabled"
>
- Days
-
</p>
</div>
</div>
Expand Down Expand Up @@ -1518,7 +1518,7 @@ exports[`issue in detector validation issues in feature query 1`] = `
<p
class="enabled"
>
- Days
-
</p>
</div>
</div>
Expand Down Expand Up @@ -1552,7 +1552,7 @@ exports[`issue in detector validation issues in feature query 1`] = `
<p
class="enabled"
>
- MB
-
</p>
</div>
</div>
Expand Down Expand Up @@ -1586,7 +1586,7 @@ exports[`issue in detector validation issues in feature query 1`] = `
<p
class="enabled"
>
- Days
-
</p>
</div>
</div>
Expand Down
Loading
Loading