mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Watcher] Use fixed_interval instead of interval (#113527)
This commit is contained in:
parent
cbc4f5235c
commit
961fe752c5
20 changed files with 232 additions and 132 deletions
|
@ -515,7 +515,6 @@ export const useField = <T, FormType = FormData, I = T>(
|
|||
if (resetValue) {
|
||||
hasBeenReset.current = true;
|
||||
const newValue = deserializeValue(updatedDefaultValue ?? defaultValue);
|
||||
// updateStateIfMounted('value', newValue);
|
||||
setValue(newValue);
|
||||
return newValue;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import { of } from 'rxjs';
|
||||
import { ComponentType } from 'enzyme';
|
||||
import { LocationDescriptorObject } from 'history';
|
||||
|
||||
import {
|
||||
docLinksServiceMock,
|
||||
uiSettingsServiceMock,
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
scopedHistoryMock,
|
||||
} from '../../../../../../src/core/public/mocks';
|
||||
import { AppContextProvider } from '../../../public/application/app_context';
|
||||
import { AppDeps } from '../../../public/application/app';
|
||||
import { LicenseStatus } from '../../../common/types/license_status';
|
||||
|
||||
class MockTimeBuckets {
|
||||
|
@ -35,7 +37,7 @@ history.createHref.mockImplementation((location: LocationDescriptorObject) => {
|
|||
return `${location.pathname}${location.search ? '?' + location.search : ''}`;
|
||||
});
|
||||
|
||||
export const mockContextValue = {
|
||||
export const mockContextValue: AppDeps = {
|
||||
licenseStatus$: of<LicenseStatus>({ valid: true }),
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
setBreadcrumbs: jest.fn(),
|
||||
|
|
|
@ -11,7 +11,7 @@ import { setup as watchCreateJsonSetup } from './watch_create_json.helpers';
|
|||
import { setup as watchCreateThresholdSetup } from './watch_create_threshold.helpers';
|
||||
import { setup as watchEditSetup } from './watch_edit.helpers';
|
||||
|
||||
export { nextTick, getRandomString, findTestSubject, TestBed } from '@kbn/test/jest';
|
||||
export { getRandomString, findTestSubject, TestBed } from '@kbn/test/jest';
|
||||
export { wrapBodyResponse, unwrapBodyResponse } from './body_response';
|
||||
export { setupEnvironment } from './setup_environment';
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import axios from 'axios';
|
||||
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
|
||||
|
||||
import { init as initHttpRequests } from './http_requests';
|
||||
import { setHttpClient, setSavedObjectsClient } from '../../../public/application/lib/api';
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ export type TestSubjects =
|
|||
| 'toEmailAddressInput'
|
||||
| 'triggerIntervalSizeInput'
|
||||
| 'watchActionAccordion'
|
||||
| 'watchActionAccordion.mockComboBox'
|
||||
| 'watchActionAccordion.toEmailAddressInput'
|
||||
| 'watchActionsPanel'
|
||||
| 'watchThresholdButton'
|
||||
| 'watchThresholdInput'
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import { registerTestBed, findTestSubject, TestBed, TestBedConfig, nextTick } from '@kbn/test/jest';
|
||||
import { registerTestBed, findTestSubject, TestBed, TestBedConfig } from '@kbn/test/jest';
|
||||
import { WatchList } from '../../../public/application/sections/watch_list/components/watch_list';
|
||||
import { ROUTES, REFRESH_INTERVALS } from '../../../common/constants';
|
||||
import { withAppContext } from './app_context.mock';
|
||||
|
@ -24,7 +24,6 @@ const initTestBed = registerTestBed(withAppContext(WatchList), testBedConfig);
|
|||
export interface WatchListTestBed extends TestBed<WatchListTestSubjects> {
|
||||
actions: {
|
||||
selectWatchAt: (index: number) => void;
|
||||
clickWatchAt: (index: number) => void;
|
||||
clickWatchActionAt: (index: number, action: 'delete' | 'edit') => void;
|
||||
searchWatches: (term: string) => void;
|
||||
advanceTimeToTableRefresh: () => Promise<void>;
|
||||
|
@ -45,18 +44,6 @@ export const setup = async (): Promise<WatchListTestBed> => {
|
|||
checkBox.simulate('change', { target: { checked: true } });
|
||||
};
|
||||
|
||||
const clickWatchAt = async (index: number) => {
|
||||
const { rows } = testBed.table.getMetaData('watchesTable');
|
||||
const watchesLink = findTestSubject(rows[index].reactWrapper, 'watchesLink');
|
||||
|
||||
await act(async () => {
|
||||
const { href } = watchesLink.props();
|
||||
testBed.router.navigateTo(href!);
|
||||
await nextTick();
|
||||
testBed.component.update();
|
||||
});
|
||||
};
|
||||
|
||||
const clickWatchActionAt = async (index: number, action: 'delete' | 'edit') => {
|
||||
const { component, table } = testBed;
|
||||
const { rows } = table.getMetaData('watchesTable');
|
||||
|
@ -95,7 +82,6 @@ export const setup = async (): Promise<WatchListTestBed> => {
|
|||
...testBed,
|
||||
actions: {
|
||||
selectWatchAt,
|
||||
clickWatchAt,
|
||||
clickWatchActionAt,
|
||||
searchWatches,
|
||||
advanceTimeToTableRefresh,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import { registerTestBed, findTestSubject, TestBed, TestBedConfig, delay } from '@kbn/test/jest';
|
||||
import { registerTestBed, findTestSubject, TestBed, TestBedConfig } from '@kbn/test/jest';
|
||||
import { WatchStatus } from '../../../public/application/sections/watch_status/components/watch_status';
|
||||
import { ROUTES } from '../../../common/constants';
|
||||
import { WATCH_ID } from './jest_constants';
|
||||
|
@ -89,9 +89,8 @@ export const setup = async (): Promise<WatchStatusTestBed> => {
|
|||
|
||||
await act(async () => {
|
||||
button.simulate('click');
|
||||
await delay(100);
|
||||
component.update();
|
||||
});
|
||||
component.update();
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils';
|
|||
|
||||
import { getExecuteDetails } from '../../__fixtures__';
|
||||
import { defaultWatch } from '../../public/application/models/watch';
|
||||
import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers';
|
||||
import { setupEnvironment, pageHelpers, wrapBodyResponse } from './helpers';
|
||||
import { WatchCreateJsonTestBed } from './helpers/watch_create_json.helpers';
|
||||
import { WATCH } from './helpers/jest_constants';
|
||||
|
||||
|
@ -19,19 +19,19 @@ describe('<JsonWatchEdit /> create route', () => {
|
|||
const { server, httpRequestsMockHelpers } = setupEnvironment();
|
||||
let testBed: WatchCreateJsonTestBed;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
server.restore();
|
||||
});
|
||||
|
||||
describe('on component mount', () => {
|
||||
beforeEach(async () => {
|
||||
testBed = await setup();
|
||||
|
||||
await act(async () => {
|
||||
const { component } = testBed;
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
|
||||
test('should set the correct page title', () => {
|
||||
|
@ -92,7 +92,6 @@ describe('<JsonWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSubmitButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
@ -141,9 +140,8 @@ describe('<JsonWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSubmitButton();
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
component.update();
|
||||
|
||||
expect(exists('sectionError')).toBe(true);
|
||||
expect(find('sectionError').text()).toContain(error.message);
|
||||
|
@ -169,7 +167,6 @@ describe('<JsonWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
@ -230,9 +227,8 @@ describe('<JsonWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
component.update();
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
||||
|
|
|
@ -9,15 +9,10 @@ import React from 'react';
|
|||
import { act } from 'react-dom/test-utils';
|
||||
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
|
||||
import axios from 'axios';
|
||||
|
||||
import { getExecuteDetails } from '../../__fixtures__';
|
||||
import { WATCH_TYPES } from '../../common/constants';
|
||||
import {
|
||||
setupEnvironment,
|
||||
pageHelpers,
|
||||
nextTick,
|
||||
wrapBodyResponse,
|
||||
unwrapBodyResponse,
|
||||
} from './helpers';
|
||||
import { setupEnvironment, pageHelpers, wrapBodyResponse, unwrapBodyResponse } from './helpers';
|
||||
import { WatchCreateThresholdTestBed } from './helpers/watch_create_threshold.helpers';
|
||||
|
||||
const WATCH_NAME = 'my_test_watch';
|
||||
|
@ -76,7 +71,9 @@ jest.mock('@elastic/eui', () => {
|
|||
// which does not produce a valid component wrapper
|
||||
EuiComboBox: (props: any) => (
|
||||
<input
|
||||
data-test-subj="mockComboBox"
|
||||
data-test-subj={props['data-test-subj'] || 'mockComboBox'}
|
||||
data-currentvalue={props.selectedOptions}
|
||||
value={props.selectedOptions[0]?.value ?? ''}
|
||||
onChange={(syntheticEvent: any) => {
|
||||
props.onChange([syntheticEvent['0']]);
|
||||
}}
|
||||
|
@ -91,7 +88,12 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
const { server, httpRequestsMockHelpers } = setupEnvironment();
|
||||
let testBed: WatchCreateThresholdTestBed;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
server.restore();
|
||||
});
|
||||
|
||||
|
@ -99,7 +101,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
beforeEach(async () => {
|
||||
testBed = await setup();
|
||||
const { component } = testBed;
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
|
||||
|
@ -159,46 +160,60 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
test('it should enable the Create button and render additional content with valid fields', async () => {
|
||||
const { form, find, component, exists } = testBed;
|
||||
|
||||
form.setInputValue('nameInput', 'my_test_watch');
|
||||
find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
form.setInputValue('watchTimeFieldSelect', '@timestamp');
|
||||
expect(find('saveWatchButton').props().disabled).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
component.update();
|
||||
form.setInputValue('nameInput', 'my_test_watch');
|
||||
find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
form.setInputValue('watchTimeFieldSelect', '@timestamp');
|
||||
});
|
||||
component.update();
|
||||
|
||||
expect(find('saveWatchButton').props().disabled).toEqual(false);
|
||||
|
||||
expect(find('saveWatchButton').props().disabled).toBe(false);
|
||||
expect(find('watchConditionTitle').text()).toBe('Match the following condition');
|
||||
expect(exists('watchVisualizationChart')).toBe(true);
|
||||
expect(exists('watchActionsPanel')).toBe(true);
|
||||
});
|
||||
|
||||
// Looks like there is an issue with using 'mockComboBox'.
|
||||
describe.skip('watch conditions', () => {
|
||||
beforeEach(() => {
|
||||
const { form, find } = testBed;
|
||||
describe('watch conditions', () => {
|
||||
beforeEach(async () => {
|
||||
const { form, find, component } = testBed;
|
||||
|
||||
// Name, index and time fields are required before the watch condition expression renders
|
||||
form.setInputValue('nameInput', 'my_test_watch');
|
||||
act(() => {
|
||||
find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
await act(async () => {
|
||||
form.setInputValue('nameInput', 'my_test_watch');
|
||||
find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
form.setInputValue('watchTimeFieldSelect', '@timestamp');
|
||||
});
|
||||
form.setInputValue('watchTimeFieldSelect', '@timestamp');
|
||||
component.update();
|
||||
});
|
||||
|
||||
test('should require a threshold value', () => {
|
||||
const { form, find } = testBed;
|
||||
test('should require a threshold value', async () => {
|
||||
const { form, find, component } = testBed;
|
||||
|
||||
// Display the threshold pannel
|
||||
act(() => {
|
||||
find('watchThresholdButton').simulate('click');
|
||||
});
|
||||
component.update();
|
||||
|
||||
await act(async () => {
|
||||
// Provide invalid value
|
||||
form.setInputValue('watchThresholdInput', '');
|
||||
});
|
||||
|
||||
// We need to wait for the debounced validation to be triggered and update the DOM
|
||||
jest.advanceTimersByTime(500);
|
||||
component.update();
|
||||
|
||||
expect(form.getErrorsMessages()).toContain('A value is required.');
|
||||
|
||||
await act(async () => {
|
||||
// Provide valid value
|
||||
form.setInputValue('watchThresholdInput', '0');
|
||||
});
|
||||
expect(form.getErrorsMessages()).toContain('A value is required.');
|
||||
component.update();
|
||||
// No need to wait as the validation errors are cleared whenever the field changes
|
||||
expect(form.getErrorsMessages().length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -209,14 +224,12 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
const { form, find, component } = testBed;
|
||||
|
||||
// Set up valid fields needed for actions component to render
|
||||
form.setInputValue('nameInput', WATCH_NAME);
|
||||
find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD);
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
component.update();
|
||||
form.setInputValue('nameInput', WATCH_NAME);
|
||||
find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]);
|
||||
form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD);
|
||||
});
|
||||
component.update();
|
||||
});
|
||||
|
||||
test('should simulate a logging action', async () => {
|
||||
|
@ -240,7 +253,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -303,7 +315,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -366,7 +377,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -431,15 +441,14 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
expect(exists('watchActionAccordion')).toBe(true);
|
||||
|
||||
// Provide valid fields and verify
|
||||
find('watchActionAccordion.mockComboBox').simulate('change', [
|
||||
find('watchActionAccordion.toEmailAddressInput').simulate('change', [
|
||||
{ label: EMAIL_RECIPIENT, value: EMAIL_RECIPIENT },
|
||||
]); // Using mocked EuiComboBox
|
||||
]);
|
||||
form.setInputValue('emailSubjectInput', EMAIL_SUBJECT);
|
||||
form.setInputValue('emailBodyInput', EMAIL_BODY);
|
||||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -532,7 +541,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -621,7 +629,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -702,7 +709,6 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSimulateButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
@ -753,20 +759,66 @@ describe('<ThresholdWatchEdit /> create route', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('watch visualize data payload', () => {
|
||||
test('should send the correct payload', async () => {
|
||||
const { form, find, component } = testBed;
|
||||
|
||||
// Set up required fields
|
||||
await act(async () => {
|
||||
form.setInputValue('nameInput', WATCH_NAME);
|
||||
find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]);
|
||||
form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD);
|
||||
});
|
||||
component.update();
|
||||
|
||||
const latestReqToGetVisualizeData = server.requests.find(
|
||||
(req) => req.method === 'POST' && req.url === '/api/watcher/watch/visualize'
|
||||
);
|
||||
if (!latestReqToGetVisualizeData) {
|
||||
throw new Error(`No request found to fetch visualize data.`);
|
||||
}
|
||||
|
||||
const requestBody = unwrapBodyResponse(latestReqToGetVisualizeData.requestBody);
|
||||
|
||||
expect(requestBody.watch).toEqual({
|
||||
id: requestBody.watch.id, // id is dynamic
|
||||
name: 'my_test_watch',
|
||||
type: 'threshold',
|
||||
isNew: true,
|
||||
isActive: true,
|
||||
actions: [],
|
||||
index: ['index1'],
|
||||
timeField: '@timestamp',
|
||||
triggerIntervalSize: 1,
|
||||
triggerIntervalUnit: 'm',
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
termOrder: 'desc',
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
hasTermsAgg: false,
|
||||
threshold: 1000,
|
||||
});
|
||||
|
||||
expect(requestBody.options.interval).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form payload', () => {
|
||||
test('should send the correct payload', async () => {
|
||||
const { form, find, component, actions } = testBed;
|
||||
|
||||
// Set up required fields
|
||||
form.setInputValue('nameInput', WATCH_NAME);
|
||||
find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox
|
||||
form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD);
|
||||
await act(async () => {
|
||||
form.setInputValue('nameInput', WATCH_NAME);
|
||||
find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]);
|
||||
form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD);
|
||||
});
|
||||
component.update();
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
component.update();
|
||||
actions.clickSubmitButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
// Verify request
|
||||
|
|
|
@ -12,7 +12,7 @@ import { getRandomString } from '@kbn/test/jest';
|
|||
|
||||
import { getWatch } from '../../__fixtures__';
|
||||
import { defaultWatch } from '../../public/application/models/watch';
|
||||
import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers';
|
||||
import { setupEnvironment, pageHelpers, wrapBodyResponse } from './helpers';
|
||||
import { WatchEditTestBed } from './helpers/watch_edit.helpers';
|
||||
import { WATCH } from './helpers/jest_constants';
|
||||
|
||||
|
@ -41,7 +41,12 @@ describe('<WatchEdit />', () => {
|
|||
const { server, httpRequestsMockHelpers } = setupEnvironment();
|
||||
let testBed: WatchEditTestBed;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
server.restore();
|
||||
});
|
||||
|
||||
|
@ -50,11 +55,7 @@ describe('<WatchEdit />', () => {
|
|||
httpRequestsMockHelpers.setLoadWatchResponse(WATCH);
|
||||
|
||||
testBed = await setup();
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
testBed.component.update();
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
|
||||
describe('on component mount', () => {
|
||||
|
@ -87,7 +88,6 @@ describe('<WatchEdit />', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSubmitButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
@ -141,12 +141,7 @@ describe('<WatchEdit />', () => {
|
|||
httpRequestsMockHelpers.setLoadWatchResponse({ watch });
|
||||
|
||||
testBed = await setup();
|
||||
|
||||
await act(async () => {
|
||||
const { component } = testBed;
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
|
||||
describe('on component mount', () => {
|
||||
|
@ -172,7 +167,6 @@ describe('<WatchEdit />', () => {
|
|||
|
||||
await act(async () => {
|
||||
actions.clickSubmitButton();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
|
|
@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils';
|
|||
import moment from 'moment';
|
||||
import { getWatchHistory } from '../../__fixtures__';
|
||||
import { ROUTES, WATCH_STATES, ACTION_STATES } from '../../common/constants';
|
||||
import { setupEnvironment, pageHelpers, nextTick } from './helpers';
|
||||
import { setupEnvironment, pageHelpers } from './helpers';
|
||||
import { WatchStatusTestBed } from './helpers/watch_status.helpers';
|
||||
import { WATCH } from './helpers/jest_constants';
|
||||
|
||||
|
@ -43,7 +43,12 @@ describe('<WatchStatus />', () => {
|
|||
const { server, httpRequestsMockHelpers } = setupEnvironment();
|
||||
let testBed: WatchStatusTestBed;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
server.restore();
|
||||
});
|
||||
|
||||
|
@ -53,11 +58,7 @@ describe('<WatchStatus />', () => {
|
|||
httpRequestsMockHelpers.setLoadWatchHistoryResponse(watchHistoryItems);
|
||||
|
||||
testBed = await setup();
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
testBed.component.update();
|
||||
});
|
||||
testBed.component.update();
|
||||
});
|
||||
|
||||
test('should set the correct page title', () => {
|
||||
|
@ -175,9 +176,8 @@ describe('<WatchStatus />', () => {
|
|||
|
||||
await act(async () => {
|
||||
confirmButton!.click();
|
||||
await nextTick();
|
||||
component.update();
|
||||
});
|
||||
component.update();
|
||||
|
||||
const latestRequest = server.requests[server.requests.length - 1];
|
||||
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { CoreSetup, Plugin, CoreStart, Capabilities } from 'kibana/public';
|
||||
import { first, map, skip } from 'rxjs/operators';
|
||||
|
||||
import { Subject, combineLatest } from 'rxjs';
|
||||
|
||||
import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public';
|
||||
|
||||
import { LicenseStatus } from '../common/types/license_status';
|
||||
|
||||
import { ILicense } from '../../licensing/public';
|
||||
import { LicenseStatus } from '../common/types/license_status';
|
||||
import { PLUGIN } from '../common/constants';
|
||||
import { Dependencies } from './types';
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { getIntervalType } from './get_interval_type';
|
||||
|
||||
describe('get interval type', () => {
|
||||
test('should detect fixed intervals', () => {
|
||||
['1ms', '1s', '1m', '1h', '1d', '21s', '7d'].forEach((interval) => {
|
||||
const intervalDetected = getIntervalType(interval);
|
||||
try {
|
||||
expect(intervalDetected).toBe('fixed_interval');
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Expected [${interval}] to be a fixed interval but got [${intervalDetected}]`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should detect calendar intervals', () => {
|
||||
['1w', '1M', '1q', '1y'].forEach((interval) => {
|
||||
const intervalDetected = getIntervalType(interval);
|
||||
try {
|
||||
expect(intervalDetected).toBe('calendar_interval');
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Expected [${interval}] to be a calendar interval but got [${intervalDetected}]`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Since 8.x we use the "fixed_interval" or "calendar_interval" parameter instead
|
||||
* of the less precise "interval". This helper parse the interval and return its type.
|
||||
* @param interval Interval value (e.g. "1d", "1w"...)
|
||||
*/
|
||||
export const getIntervalType = (interval: string): 'fixed_interval' | 'calendar_interval' => {
|
||||
// We will consider all interval as fixed except if they are
|
||||
// weekly (w), monthly (M), quarterly (q) or yearly (y)
|
||||
const intervalMetric = interval.charAt(interval.length - 1);
|
||||
if (['w', 'M', 'q', 'y'].includes(intervalMetric)) {
|
||||
return 'calendar_interval';
|
||||
}
|
||||
return 'fixed_interval';
|
||||
};
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { getIntervalType } from './get_interval_type';
|
|
@ -6,8 +6,10 @@
|
|||
*/
|
||||
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { buildInput } from '../../../../common/lib/serialization';
|
||||
import { AGG_TYPES } from '../../../../common/constants';
|
||||
import { getIntervalType } from '../lib/get_interval_type';
|
||||
|
||||
/*
|
||||
input.search.request.body.query.bool.filter.range
|
||||
|
@ -22,17 +24,6 @@ function buildRange({ rangeFrom, rangeTo, timeField }) {
|
|||
};
|
||||
}
|
||||
|
||||
function buildDateAgg({ field, interval, timeZone }) {
|
||||
return {
|
||||
date_histogram: {
|
||||
field,
|
||||
interval,
|
||||
time_zone: timeZone,
|
||||
min_doc_count: 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildAggsCount(body, dateAgg) {
|
||||
return {
|
||||
dateAgg,
|
||||
|
@ -93,7 +84,7 @@ function buildAggs(body, { aggType, termField }, dateAgg) {
|
|||
}
|
||||
}
|
||||
|
||||
export function buildVisualizeQuery(watch, visualizeOptions) {
|
||||
export function buildVisualizeQuery(watch, visualizeOptions, kibanaVersion) {
|
||||
const {
|
||||
index,
|
||||
timeWindowSize,
|
||||
|
@ -117,11 +108,22 @@ export function buildVisualizeQuery(watch, visualizeOptions) {
|
|||
termOrder,
|
||||
});
|
||||
const body = watchInput.search.request.body;
|
||||
const dateAgg = buildDateAgg({
|
||||
field: watch.timeField,
|
||||
interval: visualizeOptions.interval,
|
||||
timeZone: visualizeOptions.timezone,
|
||||
});
|
||||
const dateAgg = {
|
||||
date_histogram: {
|
||||
field: watch.timeField,
|
||||
time_zone: visualizeOptions.timezone,
|
||||
min_doc_count: 1,
|
||||
},
|
||||
};
|
||||
|
||||
if (kibanaVersion.major < 8) {
|
||||
// In 7.x we use the deprecated "interval" in date_histogram agg
|
||||
dateAgg.date_histogram.interval = visualizeOptions.interval;
|
||||
} else {
|
||||
// From 8.x we use the more precise "fixed_interval" or "calendar_interval"
|
||||
const intervalType = getIntervalType(visualizeOptions.interval);
|
||||
dateAgg.date_histogram[intervalType] = visualizeOptions.interval;
|
||||
}
|
||||
|
||||
// override the query range
|
||||
body.query.bool.filter.range = buildRange({
|
||||
|
|
|
@ -48,8 +48,8 @@ export class ThresholdWatch extends BaseWatch {
|
|||
return serializeThresholdWatch(this);
|
||||
}
|
||||
|
||||
getVisualizeQuery(visualizeOptions) {
|
||||
return buildVisualizeQuery(this, visualizeOptions);
|
||||
getVisualizeQuery(visualizeOptions, kibanaVersion) {
|
||||
return buildVisualizeQuery(this, visualizeOptions, kibanaVersion);
|
||||
}
|
||||
|
||||
formatVisualizeData(results) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { SemVer } from 'semver';
|
||||
import { CoreStart, CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
|
||||
|
||||
import { PLUGIN, INDEX_NAMES } from '../common/constants';
|
||||
|
@ -27,17 +27,19 @@ export class WatcherServerPlugin implements Plugin<void, void, any, any> {
|
|||
private readonly license: License;
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(ctx: PluginInitializerContext) {
|
||||
constructor(private ctx: PluginInitializerContext) {
|
||||
this.logger = ctx.logger.get();
|
||||
this.license = new License();
|
||||
}
|
||||
|
||||
setup({ http, getStartServices }: CoreSetup, { licensing, features }: SetupDependencies) {
|
||||
setup({ http }: CoreSetup, { features }: SetupDependencies) {
|
||||
this.license.setup({
|
||||
pluginName: PLUGIN.getI18nName(i18n),
|
||||
logger: this.logger,
|
||||
});
|
||||
|
||||
const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version);
|
||||
|
||||
const router = http.createRouter();
|
||||
const routeDependencies: RouteDependencies = {
|
||||
router,
|
||||
|
@ -45,6 +47,7 @@ export class WatcherServerPlugin implements Plugin<void, void, any, any> {
|
|||
lib: {
|
||||
handleEsError,
|
||||
},
|
||||
kibanaVersion,
|
||||
};
|
||||
|
||||
features.registerElasticsearchFeature({
|
||||
|
|
|
@ -37,6 +37,7 @@ export function registerVisualizeRoute({
|
|||
router,
|
||||
license,
|
||||
lib: { handleEsError },
|
||||
kibanaVersion,
|
||||
}: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
|
@ -48,7 +49,7 @@ export function registerVisualizeRoute({
|
|||
license.guardApiRoute(async (ctx, request, response) => {
|
||||
const watch = Watch.fromDownstreamJson(request.body.watch);
|
||||
const options = VisualizeOptions.fromDownstreamJson(request.body.options);
|
||||
const body = watch.getVisualizeQuery(options);
|
||||
const body = watch.getVisualizeQuery(options, kibanaVersion);
|
||||
|
||||
try {
|
||||
const hits = await fetchVisualizeData(ctx.core.elasticsearch.client, watch.index, body);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SemVer } from 'semver';
|
||||
import type { IRouter } from 'src/core/server';
|
||||
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
|
||||
|
@ -33,4 +34,5 @@ export interface RouteDependencies {
|
|||
lib: {
|
||||
handleEsError: typeof handleEsError;
|
||||
};
|
||||
kibanaVersion: SemVer;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue