Skip to content

Commit

Permalink
Add EngineOverviewLogic
Browse files Browse the repository at this point in the history
  • Loading branch information
cee-chen committed Nov 12, 2020
1 parent 0d5c1d2 commit 8d53954
Show file tree
Hide file tree
Showing 2 changed files with 293 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* 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 { resetContext } from 'kea';

import { mockHttpValues } from '../../../__mocks__';
jest.mock('../../../shared/http', () => ({
HttpLogic: { values: mockHttpValues },
}));
const { http } = mockHttpValues;

jest.mock('../../../shared/flash_messages', () => ({
flashAPIErrors: jest.fn(),
}));
import { flashAPIErrors } from '../../../shared/flash_messages';

jest.mock('../engine', () => ({
EngineLogic: { values: { engineName: 'some-engine' } },
}));

import { EngineOverviewLogic } from './';

describe('EngineOverviewLogic', () => {
const mockEngineMetrics = {
apiLogsUnavailable: true,
documentCount: 10,
startDate: '1970-01-30',
endDate: '1970-01-31',
operationsPerDay: [0, 0, 0, 0, 0, 0, 0],
queriesPerDay: [0, 0, 0, 0, 0, 25, 50],
totalClicks: 50,
totalQueries: 75,
};

const DEFAULT_VALUES = {
dataLoading: true,
apiLogsUnavailable: false,
documentCount: 0,
startDate: '',
endDate: '',
operationsPerDay: [],
queriesPerDay: [],
totalClicks: 0,
totalQueries: 0,
timeoutId: null,
};

const mount = () => {
resetContext({});
EngineOverviewLogic.mount();
};

beforeEach(() => {
jest.clearAllMocks();
});

it('has expected default values', () => {
mount();
expect(EngineOverviewLogic.values).toEqual(DEFAULT_VALUES);
});

describe('actions', () => {
describe('setPolledData', () => {
it('should set all received data as top-level values and set dataLoading to false', () => {
mount();
EngineOverviewLogic.actions.setPolledData(mockEngineMetrics);

expect(EngineOverviewLogic.values).toEqual({
...DEFAULT_VALUES,
...mockEngineMetrics,
dataLoading: false,
});
});
});

describe('setTimeoutId', () => {
describe('timeoutId', () => {
it('should be set to the provided value', () => {
mount();
EngineOverviewLogic.actions.setTimeoutId(123);

expect(EngineOverviewLogic.values).toEqual({
...DEFAULT_VALUES,
timeoutId: 123,
});
});
});
});

describe('pollForOverviewMetrics', () => {
it('fetches data and calls onPollingSuccess', async () => {
mount();
jest.spyOn(EngineOverviewLogic.actions, 'onPollingSuccess');
const promise = Promise.resolve(mockEngineMetrics);
http.get.mockReturnValueOnce(promise);

EngineOverviewLogic.actions.pollForOverviewMetrics();
await promise;

expect(http.get).toHaveBeenCalledWith('/api/app_search/engines/some-engine/overview');
expect(EngineOverviewLogic.actions.onPollingSuccess).toHaveBeenCalledWith(
mockEngineMetrics
);
});

it('handles errors', async () => {
mount();
const promise = Promise.reject('An error occurred');
http.get.mockReturnValue(promise);

try {
EngineOverviewLogic.actions.pollForOverviewMetrics();
await promise;
} catch {
// Do nothing
}
expect(flashAPIErrors).toHaveBeenCalledWith('An error occurred');
});
});

describe('onPollingSuccess', () => {
it('starts a polling timeout and sets data', async () => {
mount();
jest.useFakeTimers();
jest.spyOn(EngineOverviewLogic.actions, 'setTimeoutId');
jest.spyOn(EngineOverviewLogic.actions, 'setPolledData');

EngineOverviewLogic.actions.onPollingSuccess(mockEngineMetrics);

expect(setTimeout).toHaveBeenCalledWith(
EngineOverviewLogic.actions.pollForOverviewMetrics,
5000
);
expect(EngineOverviewLogic.actions.setTimeoutId).toHaveBeenCalledWith(expect.any(Number));
expect(EngineOverviewLogic.actions.setPolledData).toHaveBeenCalledWith(mockEngineMetrics);
});
});
});

describe('unmount', () => {
let unmount: Function;

beforeEach(() => {
jest.useFakeTimers();
resetContext({});
unmount = EngineOverviewLogic.mount();
});

it('clears existing polling timeouts on unmount', () => {
EngineOverviewLogic.actions.setTimeoutId(123);
unmount();
expect(clearTimeout).toHaveBeenCalled();
});

it("does not clear timeout if one hasn't been set on unmount", () => {
unmount();
expect(clearTimeout).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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 { kea, MakeLogicType } from 'kea';

import { flashAPIErrors } from '../../../shared/flash_messages';
import { HttpLogic } from '../../../shared/http';
import { EngineLogic } from '../engine';

const POLLING_DURATION = 5000;

interface EngineOverviewApiData {
apiLogsUnavailable: boolean;
documentCount: number;
startDate: string;
endDate: string;
operationsPerDay: number[];
queriesPerDay: number[];
totalClicks: number;
totalQueries: number;
}
interface EngineOverviewValues extends EngineOverviewApiData {
dataLoading: boolean;
timeoutId: number | null;
}

interface EngineOverviewActions {
setPolledData(engineMetrics: EngineOverviewApiData): EngineOverviewApiData;
setTimeoutId(timeoutId: number): { timeoutId: number };
pollForOverviewMetrics(): void;
onPollingSuccess(engineMetrics: EngineOverviewApiData): EngineOverviewApiData;
}

export const EngineOverviewLogic = kea<MakeLogicType<EngineOverviewValues, EngineOverviewActions>>({
path: ['enterprise_search', 'app_search', 'engine_overview_logic'],
actions: () => ({
setPolledData: (engineMetrics) => engineMetrics,
setTimeoutId: (timeoutId) => ({ timeoutId }),
pollForOverviewMetrics: true,
onPollingSuccess: (engineMetrics) => engineMetrics,
}),
reducers: () => ({
dataLoading: [
true,
{
setPolledData: () => false,
},
],
apiLogsUnavailable: [
false,
{
setPolledData: (_, { apiLogsUnavailable }) => apiLogsUnavailable,
},
],
startDate: [
'',
{
setPolledData: (_, { startDate }) => startDate,
},
],
endDate: [
'',
{
setPolledData: (_, { endDate }) => endDate,
},
],
queriesPerDay: [
[],
{
setPolledData: (_, { queriesPerDay }) => queriesPerDay,
},
],
operationsPerDay: [
[],
{
setPolledData: (_, { operationsPerDay }) => operationsPerDay,
},
],
totalQueries: [
0,
{
setPolledData: (_, { totalQueries }) => totalQueries,
},
],
totalClicks: [
0,
{
setPolledData: (_, { totalClicks }) => totalClicks,
},
],
documentCount: [
0,
{
setPolledData: (_, { documentCount }) => documentCount,
},
],
timeoutId: [
null,
{
setTimeoutId: (_, { timeoutId }) => timeoutId,
},
],
}),
listeners: ({ actions }) => ({
pollForOverviewMetrics: async () => {
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;

try {
const response = await http.get(`/api/app_search/engines/${engineName}/overview`);
actions.onPollingSuccess(response);
} catch (e) {
flashAPIErrors(e);
}
},
onPollingSuccess: (engineMetrics) => {
const timeoutId = window.setTimeout(actions.pollForOverviewMetrics, POLLING_DURATION);
actions.setTimeoutId(timeoutId);
actions.setPolledData(engineMetrics);
},
}),
events: ({ values }) => ({
beforeUnmount() {
if (values.timeoutId !== null) clearTimeout(values.timeoutId);
},
}),
});

0 comments on commit 8d53954

Please sign in to comment.