Skip to content

Commit

Permalink
[App Search] Use paginated API for Crawler domains (elastic#117587)
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonStoltz authored and roeehub committed Dec 16, 2021
1 parent 81a9441 commit b2d0a62
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import React from 'react';

import { shallow, ShallowWrapper } from 'enzyme';

import { EuiBasicTable, EuiButtonIcon, EuiInMemoryTable } from '@elastic/eui';
import { EuiBasicTable, EuiButtonIcon } from '@elastic/eui';

import { DEFAULT_META } from '../../../../shared/constants';
import { mountWithIntl } from '../../../../test_helpers';

import { CrawlerDomain } from '../types';
Expand Down Expand Up @@ -51,15 +52,19 @@ const domains: CrawlerDomain[] = [
const values = {
// EngineLogic
engineName: 'some-engine',
// CrawlerOverviewLogic
// CrawlerDomainsLogic
domains,
meta: DEFAULT_META,
dataLoading: false,
// AppLogic
myRole: { canManageEngineCrawler: false },
};

const actions = {
// CrawlerOverviewLogic
// CrawlerDomainsLogic
deleteDomain: jest.fn(),
fetchCrawlerDomainsData: jest.fn(),
onPaginate: jest.fn(),
};

describe('DomainsTable', () => {
Expand All @@ -69,17 +74,28 @@ describe('DomainsTable', () => {
beforeEach(() => {
jest.clearAllMocks();
});

beforeAll(() => {
setMockValues(values);
setMockActions(actions);
wrapper = shallow(<DomainsTable />);
tableContent = mountWithIntl(<DomainsTable />)
.find(EuiInMemoryTable)
.find(EuiBasicTable)
.text();
});

it('renders', () => {
expect(wrapper.find(EuiInMemoryTable)).toHaveLength(1);
expect(wrapper.find(EuiBasicTable)).toHaveLength(1);

expect(wrapper.find(EuiBasicTable).prop('pagination')).toEqual({
hidePerPageOptions: true,
pageIndex: 0,
pageSize: 10,
totalItemCount: 0,
});

wrapper.find(EuiBasicTable).simulate('change', { page: { index: 2 } });
expect(actions.onPaginate).toHaveBeenCalledWith(3);
});

describe('columns', () => {
Expand All @@ -88,7 +104,7 @@ describe('DomainsTable', () => {
});

it('renders a clickable domain url', () => {
const basicTable = wrapper.find(EuiInMemoryTable).dive().find(EuiBasicTable).dive();
const basicTable = wrapper.find(EuiBasicTable).dive();
const link = basicTable.find('[data-test-subj="CrawlerDomainURL"]').at(0);

expect(link.dive().text()).toContain('elastic.co');
Expand All @@ -110,7 +126,7 @@ describe('DomainsTable', () => {
});

describe('actions column', () => {
const getTable = () => wrapper.find(EuiInMemoryTable).dive().find(EuiBasicTable).dive();
const getTable = () => wrapper.find(EuiBasicTable).dive();
const getActions = () => getTable().find('ExpandedItemActions');
const getActionItems = () => getActions().first().dive().find('DefaultItemAction');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* 2.0.
*/

import React from 'react';
import React, { useEffect } from 'react';

import { useActions, useValues } from 'kea';

import { EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui';
import { EuiBasicTableColumn, EuiBasicTable } from '@elastic/eui';

import { i18n } from '@kbn/i18n';

Expand All @@ -18,21 +18,24 @@ import { FormattedNumber } from '@kbn/i18n/react';
import { DELETE_BUTTON_LABEL, MANAGE_BUTTON_LABEL } from '../../../../shared/constants';
import { KibanaLogic } from '../../../../shared/kibana';
import { EuiLinkTo } from '../../../../shared/react_router_helpers';
import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination';
import { AppLogic } from '../../../app_logic';
import { ENGINE_CRAWLER_DOMAIN_PATH } from '../../../routes';
import { generateEnginePath } from '../../engine';
import { CrawlerLogic } from '../crawler_logic';
import { CrawlerOverviewLogic } from '../crawler_overview_logic';
import { CrawlerDomainsLogic } from '../crawler_domains_logic';
import { CrawlerDomain } from '../types';

import { getDeleteDomainConfirmationMessage } from '../utils';

import { CustomFormattedTimestamp } from './custom_formatted_timestamp';

export const DomainsTable: React.FC = () => {
const { domains } = useValues(CrawlerLogic);
const { domains, meta, dataLoading } = useValues(CrawlerDomainsLogic);
const { fetchCrawlerDomainsData, onPaginate, deleteDomain } = useActions(CrawlerDomainsLogic);

const { deleteDomain } = useActions(CrawlerOverviewLogic);
useEffect(() => {
fetchCrawlerDomainsData();
}, [meta.page.current]);

const {
myRole: { canManageEngineCrawler },
Expand Down Expand Up @@ -125,5 +128,16 @@ export const DomainsTable: React.FC = () => {
columns.push(actionsColumn);
}

return <EuiInMemoryTable items={domains} columns={columns} />;
return (
<EuiBasicTable
loading={dataLoading}
items={domains}
columns={columns}
pagination={{
...convertMetaToPagination(meta),
hidePerPageOptions: true,
}}
onChange={handlePageChange(onPaginate)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* 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 {
LogicMounter,
mockHttpValues,
mockFlashMessageHelpers,
} from '../../../__mocks__/kea_logic';
import '../../__mocks__/engine_logic.mock';

import { nextTick } from '@kbn/test/jest';

import { Meta } from '../../../../../common/types';

import { DEFAULT_META } from '../../../shared/constants';

import { CrawlerDomainsLogic, CrawlerDomainsValues } from './crawler_domains_logic';
import { CrawlerDataFromServer, CrawlerDomain, CrawlerDomainFromServer } from './types';
import { crawlerDataServerToClient } from './utils';

const DEFAULT_VALUES: CrawlerDomainsValues = {
dataLoading: true,
domains: [],
meta: DEFAULT_META,
};

const crawlerDataResponse: CrawlerDataFromServer = {
domains: [
{
id: '507f1f77bcf86cd799439011',
name: 'elastic.co',
created_on: 'Mon, 31 Aug 2020 17:00:00 +0000',
document_count: 13,
sitemaps: [],
entry_points: [],
crawl_rules: [],
deduplication_enabled: false,
deduplication_fields: ['title'],
available_deduplication_fields: ['title', 'description'],
},
],
events: [],
most_recent_crawl_request: null,
};

const clientCrawlerData = crawlerDataServerToClient(crawlerDataResponse);

const domainsFromServer: CrawlerDomainFromServer[] = [
{
name: 'http://www.example.com',
created_on: 'foo',
document_count: 10,
id: '1',
crawl_rules: [],
entry_points: [],
sitemaps: [],
deduplication_enabled: true,
deduplication_fields: [],
available_deduplication_fields: [],
},
];

const domains: CrawlerDomain[] = [
{
createdOn: 'foo',
documentCount: 10,
id: '1',
url: 'http://www.example.com',
crawlRules: [],
entryPoints: [],
sitemaps: [],
deduplicationEnabled: true,
deduplicationFields: [],
availableDeduplicationFields: [],
},
];

const meta: Meta = {
page: {
current: 2,
size: 100,
total_pages: 5,
total_results: 500,
},
};

describe('CrawlerDomainsLogic', () => {
const { mount } = new LogicMounter(CrawlerDomainsLogic);
const { http } = mockHttpValues;
const { flashAPIErrors } = mockFlashMessageHelpers;

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

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

describe('actions', () => {
describe('onReceiveData', () => {
it('sets state from an API call', () => {
mount();

CrawlerDomainsLogic.actions.onReceiveData(domains, meta);

expect(CrawlerDomainsLogic.values).toEqual({
...DEFAULT_VALUES,
domains,
meta,
dataLoading: false,
});
});
});

describe('onPaginate', () => {
it('sets dataLoading to true & sets meta state', () => {
mount({ dataLoading: false });
CrawlerDomainsLogic.actions.onPaginate(5);

expect(CrawlerDomainsLogic.values).toEqual({
...DEFAULT_VALUES,
dataLoading: true,
meta: {
...DEFAULT_META,
page: {
...DEFAULT_META.page,
current: 5,
},
},
});
});
});
});

describe('listeners', () => {
describe('fetchCrawlerDomainsData', () => {
it('updates logic with data that has been converted from server to client', async () => {
mount();
jest.spyOn(CrawlerDomainsLogic.actions, 'onReceiveData');

http.get.mockReturnValueOnce(
Promise.resolve({
results: domainsFromServer,
meta,
})
);

CrawlerDomainsLogic.actions.fetchCrawlerDomainsData();
await nextTick();

expect(http.get).toHaveBeenCalledWith(
'/internal/app_search/engines/some-engine/crawler/domains',
{
query: { 'page[current]': 1, 'page[size]': 10 },
}
);
expect(CrawlerDomainsLogic.actions.onReceiveData).toHaveBeenCalledWith(domains, meta);
});

it('displays any errors to the user', async () => {
mount();
http.get.mockReturnValueOnce(Promise.reject('error'));

CrawlerDomainsLogic.actions.fetchCrawlerDomainsData();
await nextTick();

expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});

describe('deleteDomain', () => {
it('deletes the domain and then calls crawlerDomainDeleted with the response', async () => {
jest.spyOn(CrawlerDomainsLogic.actions, 'crawlerDomainDeleted');
http.delete.mockReturnValue(Promise.resolve(crawlerDataResponse));

CrawlerDomainsLogic.actions.deleteDomain({ id: '1234' } as CrawlerDomain);
await nextTick();

expect(http.delete).toHaveBeenCalledWith(
'/internal/app_search/engines/some-engine/crawler/domains/1234',
{
query: { respond_with: 'crawler_details' },
}
);
expect(CrawlerDomainsLogic.actions.crawlerDomainDeleted).toHaveBeenCalledWith(
clientCrawlerData
);
});

it('calls flashApiErrors when there is an error', async () => {
http.delete.mockReturnValue(Promise.reject('error'));

CrawlerDomainsLogic.actions.deleteDomain({ id: '1234' } as CrawlerDomain);
await nextTick();

expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});
});
});
Loading

0 comments on commit b2d0a62

Please sign in to comment.