Skip to content

Commit

Permalink
Verify if there are indices before leverage search profile
Browse files Browse the repository at this point in the history
  • Loading branch information
SoniaSanzV committed Nov 25, 2024
1 parent d0f0809 commit 5832a76
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useRef, memo, useCallback, useState } from 'react';
import React, { useRef, memo, useCallback, useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiForm,
Expand All @@ -16,11 +16,12 @@ import {
EuiFlexGroup,
EuiSpacer,
EuiFlexItem,
EuiToolTip,
} from '@elastic/eui';

import { decompressFromEncodedURIComponent } from 'lz-string';

import { useRequestProfile } from '../../hooks';
import { useIndices, useRequestProfile } from '../../hooks';
import { useAppContext } from '../../contexts/app_context';
import { useProfilerActionContext } from '../../contexts/profiler_context';
import { Editor, type EditorProps } from './editor';
Expand All @@ -41,6 +42,7 @@ const INITIAL_EDITOR_VALUE = `{
export const ProfileQueryEditor = memo(() => {
const editorPropsRef = useRef<EditorProps>(null as any);
const indexInputRef = useRef<HTMLInputElement>(null as any);
const [hasIndices, setHasIndices] = useState(false);

const dispatch = useProfilerActionContext();

Expand Down Expand Up @@ -86,6 +88,17 @@ export const ProfileQueryEditor = memo(() => {
);
const licenseEnabled = getLicenseStatus().valid;

const indices = useIndices();

const getHasIndices = useCallback(async () => {
const response = await indices();
setHasIndices(response.hasIndices);
}, [indices]);

useEffect(() => {
getHasIndices();
}, [getHasIndices]);

return (
<EuiFlexGroup responsive={false} gutterSize="none" direction="column">
{/* Form */}
Expand Down Expand Up @@ -135,18 +148,40 @@ export const ProfileQueryEditor = memo(() => {
<EuiSpacer size="s" />
</EuiFlexItem>
<EuiFlexItem grow={5}>
<EuiButton
data-test-subj="profileButton"
fill
disabled={!licenseEnabled}
onClick={() => handleProfileClick()}
>
<EuiText>
{i18n.translate('xpack.searchProfiler.formProfileButtonLabel', {
defaultMessage: 'Profile',
{licenseEnabled && !hasIndices ? (
<EuiToolTip
position="top"
content={i18n.translate('xpack.searchProfiler.formProfileButtonTooltip', {
defaultMessage: 'An index must be created before leveraging Search Profiler',
})}
</EuiText>
</EuiButton>
>
<EuiButton
data-test-subj="disabledprofileButton"
fill
disabled={true}
onClick={() => {}}
>
<EuiText>
{i18n.translate('xpack.searchProfiler.formProfileButtonLabel', {
defaultMessage: 'Profile',
})}
</EuiText>
</EuiButton>
</EuiToolTip>
) : (
<EuiButton
data-test-subj="profileButton"
fill
disabled={!licenseEnabled}
onClick={() => handleProfileClick()}
>
<EuiText>
{i18n.translate('xpack.searchProfiler.formProfileButtonLabel', {
defaultMessage: 'Profile',
})}
</EuiText>
</EuiButton>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*/

export { useRequestProfile } from './use_request_profile';
export { useIndices } from './use_indices';
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { act } from 'react-dom/test-utils';
import { renderHook } from '@testing-library/react-hooks';
import { useAppContext } from '../contexts/app_context';
import { useIndices } from './use_indices';
import { waitFor } from '@testing-library/dom';

jest.mock('../contexts/app_context');

describe('useIndices', () => {
let httpMock: { get: jest.Mock };

beforeEach(() => {
httpMock = {
get: jest.fn(),
};

(useAppContext as jest.Mock).mockReturnValue({ http: httpMock });
});

it('should return true if response is ok and has indices', async () => {
httpMock.get.mockResolvedValue({ ok: true, hasIndices: true });

const { result } = renderHook(() => useIndices());

act(() => {
result.current().then((res: any) => {
expect(res.hasIndices).toBe(true);
});
});

await waitFor(() => expect(httpMock.get).toHaveBeenCalled());
});

it('should return false if response is ok and has indices', async () => {
httpMock.get.mockResolvedValue({ ok: true, hasIndices: false });

const { result } = renderHook(() => useIndices());

act(() => {
result.current().then((res: any) => {
expect(res.hasIndices).toBe(false);
});
});

await waitFor(() => expect(httpMock.get).toHaveBeenCalled());
});

it('should return hasIndices as false when API response is not ok', async () => {
httpMock.get.mockResolvedValue({ ok: false, err: { msg: 'Error message' } });

const { result } = renderHook(() => useIndices());

act(() => {
result.current().then((res: any) => {
expect(res.hasIndices).toBe(false);
});
});

await waitFor(() => expect(httpMock.get).toHaveBeenCalled());
});

it('should throw an error when the API call fails', async () => {
httpMock.get.mockRejectedValue(new Error('Network error'));

const { result } = renderHook(() => useIndices());

await expect(result.current()).rejects.toThrow('Error fetching indices:');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useAppContext } from '../contexts/app_context';

interface ReturnValue {
hasIndices: boolean;
}

export const useIndices = () => {
const { http } = useAppContext();
return async (): Promise<ReturnValue> => {
try {
const response = await http.get<
{ ok: true; hasIndices: boolean } | { ok: false; err: { msg: string } }
>('../api/searchprofiler/getIndices');

return { hasIndices: response.ok ? response.hasIndices : false };
} catch (e) {
throw new Error('Error fetching indices:', e);
}
};
};
43 changes: 43 additions & 0 deletions x-pack/plugins/searchprofiler/server/routes/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,49 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) =
log.error(err);
const { statusCode, body: errorBody } = err;

return response.customError({
statusCode: statusCode || 500,
body: errorBody
? {
message: errorBody.error?.reason,
attributes: errorBody,
}
: err,
});
}
}
);
router.get(
{
path: '/api/searchprofiler/getIndices',
validate: false,
},
async (ctx, _request, response) => {
const currentLicenseStatus = getLicenseStatus();
if (!currentLicenseStatus.valid) {
return response.forbidden({
body: {
message: currentLicenseStatus.message!,
},
});
}

try {
const client = (await ctx.core).elasticsearch.client.asCurrentUser;
const resp = await client.cat.indices({ format: 'json' });

const hasIndices = resp.length > 0;

return response.ok({
body: {
ok: true,
hasIndices,
},
});
} catch (err) {
log.error(err);
const { statusCode, body: errorBody } = err;

return response.customError({
statusCode: statusCode || 500,
body: errorBody
Expand Down

0 comments on commit 5832a76

Please sign in to comment.