mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[App Search] Added an EntryPointsTable (#108316)
This commit is contained in:
parent
caaa76feab
commit
4d7fd0a0ad
14 changed files with 679 additions and 4 deletions
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { EuiFieldText } from '@elastic/eui';
|
||||
|
||||
import { GenericEndpointInlineEditableTable } from '../../../../shared/tables/generic_endpoint_inline_editable_table';
|
||||
|
||||
import { mountWithIntl } from '../../../../test_helpers';
|
||||
|
||||
import { EntryPointsTable } from './entry_points_table';
|
||||
|
||||
describe('EntryPointsTable', () => {
|
||||
const engineName = 'my-engine';
|
||||
const entryPoints = [
|
||||
{ id: '1', value: '/whatever' },
|
||||
{ id: '2', value: '/foo' },
|
||||
];
|
||||
const domain = {
|
||||
createdOn: '2018-01-01T00:00:00.000Z',
|
||||
documentCount: 10,
|
||||
id: '6113e1407a2f2e6f42489794',
|
||||
url: 'https://www.elastic.co',
|
||||
crawlRules: [],
|
||||
entryPoints,
|
||||
sitemaps: [],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
);
|
||||
|
||||
expect(wrapper.find(GenericEndpointInlineEditableTable).exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('the first and only column in the table', () => {
|
||||
it('shows the value of an entry point', () => {
|
||||
const entryPoint = { id: '1', value: '/whatever' };
|
||||
|
||||
const wrapper = shallow(
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
);
|
||||
|
||||
const columns = wrapper.find(GenericEndpointInlineEditableTable).prop('columns');
|
||||
const column = shallow(<div>{columns[0].render(entryPoint)}</div>);
|
||||
expect(column.html()).toContain('/whatever');
|
||||
});
|
||||
|
||||
it('can show the value of an entry point as editable', () => {
|
||||
const entryPoint = { id: '1', value: '/whatever' };
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = shallow(
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
);
|
||||
|
||||
const columns = wrapper.find(GenericEndpointInlineEditableTable).prop('columns');
|
||||
const column = shallow(
|
||||
<div>
|
||||
{columns[0].editingRender(entryPoint, onChange, { isInvalid: false, isLoading: false })}
|
||||
</div>
|
||||
);
|
||||
|
||||
const textField = column.find(EuiFieldText);
|
||||
expect(textField.props()).toEqual(
|
||||
expect.objectContaining({
|
||||
value: '/whatever',
|
||||
disabled: false, // It would be disabled if isLoading is true
|
||||
isInvalid: false,
|
||||
prepend: 'https://www.elastic.co',
|
||||
})
|
||||
);
|
||||
|
||||
textField.simulate('change', { target: { value: '/foo' } });
|
||||
expect(onChange).toHaveBeenCalledWith('/foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('routes', () => {
|
||||
it('can calculate an update and delete route correctly', () => {
|
||||
const wrapper = shallow(
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
);
|
||||
|
||||
const table = wrapper.find(GenericEndpointInlineEditableTable);
|
||||
|
||||
const entryPoint = { id: '1', value: '/whatever' };
|
||||
expect(table.prop('deleteRoute')(entryPoint)).toEqual(
|
||||
'/api/app_search/engines/my-engine/crawler/domains/6113e1407a2f2e6f42489794/entry_points/1'
|
||||
);
|
||||
expect(table.prop('updateRoute')(entryPoint)).toEqual(
|
||||
'/api/app_search/engines/my-engine/crawler/domains/6113e1407a2f2e6f42489794/entry_points/1'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows a no items message whem there are no entry points to show', () => {
|
||||
const wrapper = shallow(
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
);
|
||||
|
||||
const editNewItems = jest.fn();
|
||||
const table = wrapper.find(GenericEndpointInlineEditableTable);
|
||||
const message = mountWithIntl(<div>{table.prop('noItemsMessage')!(editNewItems)}</div>);
|
||||
expect(message.html()).toContain('There are no existing entry points.');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { useActions } from 'kea';
|
||||
|
||||
import { EuiFieldText, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { GenericEndpointInlineEditableTable } from '../../../../shared/tables/generic_endpoint_inline_editable_table';
|
||||
import { InlineEditableTableColumn } from '../../../../shared/tables/inline_editable_table/types';
|
||||
import { ItemWithAnID } from '../../../../shared/tables/types';
|
||||
import { DOCS_PREFIX } from '../../../routes';
|
||||
import { CrawlerDomain, EntryPoint } from '../types';
|
||||
|
||||
import { EntryPointsTableLogic } from './entry_points_table_logic';
|
||||
|
||||
interface EntryPointsTableProps {
|
||||
domain: CrawlerDomain;
|
||||
engineName: string;
|
||||
items: EntryPoint[];
|
||||
}
|
||||
|
||||
export const EntryPointsTable: React.FC<EntryPointsTableProps> = ({
|
||||
domain,
|
||||
engineName,
|
||||
items,
|
||||
}) => {
|
||||
const { onAdd, onDelete, onUpdate } = useActions(EntryPointsTableLogic);
|
||||
const field = 'value';
|
||||
|
||||
const columns: Array<InlineEditableTableColumn<ItemWithAnID>> = [
|
||||
{
|
||||
editingRender: (entryPoint, onChange, { isInvalid, isLoading }) => (
|
||||
<EuiFieldText
|
||||
fullWidth
|
||||
value={(entryPoint as EntryPoint)[field]}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={isLoading}
|
||||
isInvalid={isInvalid}
|
||||
prepend={domain.url}
|
||||
/>
|
||||
),
|
||||
render: (entryPoint) => (
|
||||
<EuiText size="s">
|
||||
{domain.url}
|
||||
{(entryPoint as EntryPoint)[field]}
|
||||
</EuiText>
|
||||
),
|
||||
name: i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.urlTableHead',
|
||||
{ defaultMessage: 'URL' }
|
||||
),
|
||||
field,
|
||||
},
|
||||
];
|
||||
|
||||
const entryPointsRoute = `/api/app_search/engines/${engineName}/crawler/domains/${domain.id}/entry_points`;
|
||||
|
||||
const getEntryPointRoute = (entryPoint: EntryPoint) =>
|
||||
`/api/app_search/engines/${engineName}/crawler/domains/${domain.id}/entry_points/${entryPoint.id}`;
|
||||
|
||||
return (
|
||||
<GenericEndpointInlineEditableTable
|
||||
addButtonText={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.addButtonLabel',
|
||||
{ defaultMessage: 'Add entry point' }
|
||||
)}
|
||||
columns={columns}
|
||||
description={
|
||||
<p>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.description', {
|
||||
defaultMessage:
|
||||
'Include the most important URLs for your website here. Entry point URLs will be the first pages to be indexed and processed for links to other pages.',
|
||||
})}{' '}
|
||||
<EuiLink
|
||||
href={`${DOCS_PREFIX}/crawl-web-content.html#crawl-web-content-manage-entry-points`}
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.learnMoreLinkText',
|
||||
{ defaultMessage: 'Learn more about entry points.' }
|
||||
)}
|
||||
</EuiLink>
|
||||
</p>
|
||||
}
|
||||
instanceId="EntryPointsTable"
|
||||
items={items}
|
||||
lastItemWarning={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.lastItemMessage',
|
||||
{ defaultMessage: 'The crawler requires at least one entry point.' }
|
||||
)}
|
||||
// Since canRemoveLastItem is false, the only time noItemsMessage would be displayed is if the last entry point was deleted via the API.
|
||||
noItemsMessage={(editNewItem) => (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<EuiTitle size="m">
|
||||
<h4>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.emptyMessageTitle',
|
||||
{
|
||||
defaultMessage: 'There are no existing entry points.',
|
||||
}
|
||||
)}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.emptyMessageDescription"
|
||||
defaultMessage="{link} to specify an entry point
|
||||
for the crawler"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink onClick={editNewItem}>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.emptyMessageLinkText',
|
||||
{ defaultMessage: 'Add an entry point' }
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
addRoute={entryPointsRoute}
|
||||
canRemoveLastItem={false}
|
||||
deleteRoute={getEntryPointRoute}
|
||||
updateRoute={getEntryPointRoute}
|
||||
dataProperty="entry_points"
|
||||
onAdd={onAdd}
|
||||
onDelete={onDelete}
|
||||
onUpdate={onUpdate}
|
||||
title={i18n.translate('xpack.enterpriseSearch.appSearch.crawler.entryPointsTable.title', {
|
||||
defaultMessage: 'Entry points',
|
||||
})}
|
||||
disableReordering
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('../crawler_single_domain_logic', () => ({
|
||||
CrawlerSingleDomainLogic: {
|
||||
actions: {
|
||||
updateEntryPoints: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
import { LogicMounter, mockFlashMessageHelpers } from '../../../../__mocks__/kea_logic';
|
||||
|
||||
import { CrawlerSingleDomainLogic } from '../crawler_single_domain_logic';
|
||||
|
||||
import { EntryPointsTableLogic } from './entry_points_table_logic';
|
||||
|
||||
describe('EntryPointsTableLogic', () => {
|
||||
const { mount } = new LogicMounter(EntryPointsTableLogic);
|
||||
const { clearFlashMessages, flashSuccessToast } = mockFlashMessageHelpers;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('listeners', () => {
|
||||
describe('onAdd', () => {
|
||||
it('should update the entry points for the current domain, and clear flash messages', () => {
|
||||
const entryThatWasAdded = { id: '2', value: 'bar' };
|
||||
const updatedEntries = [
|
||||
{ id: '1', value: 'foo' },
|
||||
{ id: '2', value: 'bar' },
|
||||
];
|
||||
mount();
|
||||
EntryPointsTableLogic.actions.onAdd(entryThatWasAdded, updatedEntries);
|
||||
expect(CrawlerSingleDomainLogic.actions.updateEntryPoints).toHaveBeenCalledWith(
|
||||
updatedEntries
|
||||
);
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDelete', () => {
|
||||
it('should update the entry points for the current domain, clear flash messages, and show a success toast', () => {
|
||||
const entryThatWasDeleted = { id: '2', value: 'bar' };
|
||||
const updatedEntries = [{ id: '1', value: 'foo' }];
|
||||
mount();
|
||||
EntryPointsTableLogic.actions.onDelete(entryThatWasDeleted, updatedEntries);
|
||||
expect(CrawlerSingleDomainLogic.actions.updateEntryPoints).toHaveBeenCalledWith(
|
||||
updatedEntries
|
||||
);
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
expect(flashSuccessToast).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onUpdate', () => {
|
||||
it('should update the entry points for the current domain, clear flash messages, and show a success toast', () => {
|
||||
const entryThatWasUpdated = { id: '2', value: 'baz' };
|
||||
const updatedEntries = [
|
||||
{ id: '1', value: 'foo' },
|
||||
{ id: '2', value: 'baz' },
|
||||
];
|
||||
mount();
|
||||
EntryPointsTableLogic.actions.onUpdate(entryThatWasUpdated, updatedEntries);
|
||||
expect(CrawlerSingleDomainLogic.actions.updateEntryPoints).toHaveBeenCalledWith(
|
||||
updatedEntries
|
||||
);
|
||||
expect(clearFlashMessages).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 { kea, MakeLogicType } from 'kea';
|
||||
|
||||
import { clearFlashMessages, flashSuccessToast } from '../../../../shared/flash_messages';
|
||||
|
||||
import { CrawlerSingleDomainLogic } from '../crawler_single_domain_logic';
|
||||
|
||||
import { EntryPoint } from '../types';
|
||||
|
||||
interface EntryPointsTableValues {
|
||||
dataLoading: boolean;
|
||||
}
|
||||
|
||||
interface EntryPointsTableActions {
|
||||
onAdd(
|
||||
entryPoint: EntryPoint,
|
||||
entryPoints: EntryPoint[]
|
||||
): { entryPoint: EntryPoint; entryPoints: EntryPoint[] };
|
||||
onDelete(
|
||||
entryPoint: EntryPoint,
|
||||
entryPoints: EntryPoint[]
|
||||
): { entryPoint: EntryPoint; entryPoints: EntryPoint[] };
|
||||
onUpdate(
|
||||
entryPoint: EntryPoint,
|
||||
entryPoints: EntryPoint[]
|
||||
): { entryPoint: EntryPoint; entryPoints: EntryPoint[] };
|
||||
}
|
||||
|
||||
export const EntryPointsTableLogic = kea<
|
||||
MakeLogicType<EntryPointsTableValues, EntryPointsTableActions>
|
||||
>({
|
||||
path: ['enterprise_search', 'app_search', 'crawler', 'entry_points_table'],
|
||||
actions: () => ({
|
||||
onAdd: (entryPoint, entryPoints) => ({ entryPoint, entryPoints }),
|
||||
onDelete: (entryPoint, entryPoints) => ({ entryPoint, entryPoints }),
|
||||
onUpdate: (entryPoint, entryPoints) => ({ entryPoint, entryPoints }),
|
||||
}),
|
||||
listeners: () => ({
|
||||
onAdd: ({ entryPoints }) => {
|
||||
CrawlerSingleDomainLogic.actions.updateEntryPoints(entryPoints);
|
||||
clearFlashMessages();
|
||||
},
|
||||
onDelete: ({ entryPoint, entryPoints }) => {
|
||||
CrawlerSingleDomainLogic.actions.updateEntryPoints(entryPoints);
|
||||
clearFlashMessages();
|
||||
flashSuccessToast(`Entry point "${entryPoint.value}" was removed.`);
|
||||
},
|
||||
onUpdate: ({ entryPoints }) => {
|
||||
CrawlerSingleDomainLogic.actions.updateEntryPoints(entryPoints);
|
||||
clearFlashMessages();
|
||||
},
|
||||
}),
|
||||
});
|
|
@ -11,22 +11,24 @@ import { useParams } from 'react-router-dom';
|
|||
|
||||
import { useActions, useValues } from 'kea';
|
||||
|
||||
import { EuiCode, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { EuiCode, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { getEngineBreadcrumbs } from '../engine';
|
||||
import { EngineLogic, getEngineBreadcrumbs } from '../engine';
|
||||
import { AppSearchPageTemplate } from '../layout';
|
||||
|
||||
import { CrawlerStatusBanner } from './components/crawler_status_banner';
|
||||
import { CrawlerStatusIndicator } from './components/crawler_status_indicator/crawler_status_indicator';
|
||||
import { DeleteDomainPanel } from './components/delete_domain_panel';
|
||||
import { EntryPointsTable } from './components/entry_points_table';
|
||||
import { ManageCrawlsPopover } from './components/manage_crawls_popover/manage_crawls_popover';
|
||||
import { CRAWLER_TITLE } from './constants';
|
||||
import { CrawlerSingleDomainLogic } from './crawler_single_domain_logic';
|
||||
|
||||
export const CrawlerSingleDomain: React.FC = () => {
|
||||
const { domainId } = useParams() as { domainId: string };
|
||||
const { engineName } = EngineLogic.values;
|
||||
|
||||
const { dataLoading, domain } = useValues(CrawlerSingleDomainLogic);
|
||||
|
||||
|
@ -51,6 +53,14 @@ export const CrawlerSingleDomain: React.FC = () => {
|
|||
>
|
||||
<CrawlerStatusBanner />
|
||||
<EuiSpacer size="l" />
|
||||
{domain && (
|
||||
<>
|
||||
<EuiPanel paddingSize="l" hasBorder>
|
||||
<EntryPointsTable domain={domain} engineName={engineName} items={domain.entryPoints} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="xl" />
|
||||
</>
|
||||
)}
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
{i18n.translate(
|
||||
|
|
|
@ -51,6 +51,36 @@ describe('CrawlerSingleDomainLogic', () => {
|
|||
expect(CrawlerSingleDomainLogic.values.domain).toEqual(domain);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateEntryPoints', () => {
|
||||
beforeEach(() => {
|
||||
mount({
|
||||
domain: {
|
||||
id: '507f1f77bcf86cd799439011',
|
||||
entryPoints: [],
|
||||
},
|
||||
});
|
||||
|
||||
CrawlerSingleDomainLogic.actions.updateEntryPoints([
|
||||
{
|
||||
id: '1234',
|
||||
value: '/',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update the entry points on the domain', () => {
|
||||
expect(CrawlerSingleDomainLogic.values.domain).toEqual({
|
||||
id: '507f1f77bcf86cd799439011',
|
||||
entryPoints: [
|
||||
{
|
||||
id: '1234',
|
||||
value: '/',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listeners', () => {
|
||||
|
|
|
@ -14,7 +14,7 @@ import { KibanaLogic } from '../../../shared/kibana';
|
|||
import { ENGINE_CRAWLER_PATH } from '../../routes';
|
||||
import { EngineLogic, generateEnginePath } from '../engine';
|
||||
|
||||
import { CrawlerDomain } from './types';
|
||||
import { CrawlerDomain, EntryPoint } from './types';
|
||||
import { crawlerDomainServerToClient, getDeleteDomainSuccessMessage } from './utils';
|
||||
|
||||
export interface CrawlerSingleDomainValues {
|
||||
|
@ -26,6 +26,7 @@ interface CrawlerSingleDomainActions {
|
|||
deleteDomain(domain: CrawlerDomain): { domain: CrawlerDomain };
|
||||
fetchDomainData(domainId: string): { domainId: string };
|
||||
onReceiveDomainData(domain: CrawlerDomain): { domain: CrawlerDomain };
|
||||
updateEntryPoints(entryPoints: EntryPoint[]): { entryPoints: EntryPoint[] };
|
||||
}
|
||||
|
||||
export const CrawlerSingleDomainLogic = kea<
|
||||
|
@ -36,6 +37,7 @@ export const CrawlerSingleDomainLogic = kea<
|
|||
deleteDomain: (domain) => ({ domain }),
|
||||
fetchDomainData: (domainId) => ({ domainId }),
|
||||
onReceiveDomainData: (domain) => ({ domain }),
|
||||
updateEntryPoints: (entryPoints) => ({ entryPoints }),
|
||||
},
|
||||
reducers: {
|
||||
dataLoading: [
|
||||
|
@ -48,6 +50,8 @@ export const CrawlerSingleDomainLogic = kea<
|
|||
null,
|
||||
{
|
||||
onReceiveDomainData: (_, { domain }) => domain,
|
||||
updateEntryPoints: (currentDomain, { entryPoints }) =>
|
||||
({ ...currentDomain, entryPoints } as CrawlerDomain),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -32,6 +32,7 @@ export interface GenericEndpointInlineEditableTableProps
|
|||
onDelete(item: ItemWithAnID, items: ItemWithAnID[]): void;
|
||||
onUpdate(item: ItemWithAnID, items: ItemWithAnID[]): void;
|
||||
onReorder?(items: ItemWithAnID[]): void;
|
||||
disableReordering?: boolean;
|
||||
}
|
||||
|
||||
export const GenericEndpointInlineEditableTable = ({
|
||||
|
|
|
@ -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 { GenericEndpointInlineEditableTable } from './generic_endpoint_inline_editable_table';
|
|
@ -33,6 +33,7 @@ export interface InlineEditableTableProps<Item extends ItemWithAnID> {
|
|||
canRemoveLastItem?: boolean;
|
||||
className?: string;
|
||||
description?: React.ReactNode;
|
||||
disableReordering?: boolean;
|
||||
isLoading?: boolean;
|
||||
lastItemWarning?: string;
|
||||
noItemsMessage?: (editNewItem: () => void) => React.ReactNode;
|
||||
|
@ -170,6 +171,7 @@ export const InlineEditableTableContents = <Item extends ItemWithAnID>({
|
|||
noItemsMessage={noItemsMessage(editNewItem)}
|
||||
onReorder={reorderItems}
|
||||
disableDragging={isEditing}
|
||||
{...rest}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
*/
|
||||
|
||||
export type ItemWithAnID = {
|
||||
id: number | null;
|
||||
id: number | string | null;
|
||||
created_at?: string;
|
||||
} & object;
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 { mockDependencies, mockRequestHandler, MockRouter } from '../../__mocks__';
|
||||
|
||||
import { registerCrawlerEntryPointRoutes } from './crawler_entry_points';
|
||||
|
||||
describe('crawler entry point routes', () => {
|
||||
describe('POST /api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points', () => {
|
||||
let mockRouter: MockRouter;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockRouter = new MockRouter({
|
||||
method: 'post',
|
||||
path: '/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points',
|
||||
});
|
||||
|
||||
registerCrawlerEntryPointRoutes({
|
||||
...mockDependencies,
|
||||
router: mockRouter.router,
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a request to enterprise search', () => {
|
||||
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('validates correctly with required params', () => {
|
||||
const request = {
|
||||
params: { engineName: 'some-engine', domainId: '1234' },
|
||||
body: {
|
||||
value: 'test',
|
||||
},
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails otherwise', () => {
|
||||
const request = { params: {}, body: {} };
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}', () => {
|
||||
let mockRouter: MockRouter;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockRouter = new MockRouter({
|
||||
method: 'put',
|
||||
path:
|
||||
'/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}',
|
||||
});
|
||||
|
||||
registerCrawlerEntryPointRoutes({
|
||||
...mockDependencies,
|
||||
router: mockRouter.router,
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a request to enterprise search', () => {
|
||||
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points/:entryPointId',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('validates correctly with required params', () => {
|
||||
const request = {
|
||||
params: { engineName: 'some-engine', domainId: '1234', entryPointId: '5678' },
|
||||
body: {
|
||||
value: 'test',
|
||||
},
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails otherwise', () => {
|
||||
const request = { params: {}, body: {} };
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}', () => {
|
||||
let mockRouter: MockRouter;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockRouter = new MockRouter({
|
||||
method: 'delete',
|
||||
path:
|
||||
'/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}',
|
||||
});
|
||||
|
||||
registerCrawlerEntryPointRoutes({
|
||||
...mockDependencies,
|
||||
router: mockRouter.router,
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a request to enterprise search', () => {
|
||||
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points/:entryPointId',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('validates correctly with required params', () => {
|
||||
const request = {
|
||||
params: { engineName: 'some-engine', domainId: '1234', entryPointId: '5678' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails otherwise', () => {
|
||||
const request = { params: {} };
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { schema } from '@kbn/config-schema';
|
||||
|
||||
import { RouteDependencies } from '../../plugin';
|
||||
|
||||
export function registerCrawlerEntryPointRoutes({
|
||||
router,
|
||||
enterpriseSearchRequestHandler,
|
||||
}: RouteDependencies) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
engineName: schema.string(),
|
||||
domainId: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
value: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
{
|
||||
path:
|
||||
'/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
engineName: schema.string(),
|
||||
domainId: schema.string(),
|
||||
entryPointId: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
value: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points/:entryPointId',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
router.delete(
|
||||
{
|
||||
path:
|
||||
'/api/app_search/engines/{engineName}/crawler/domains/{domainId}/entry_points/{entryPointId}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
engineName: schema.string(),
|
||||
domainId: schema.string(),
|
||||
entryPointId: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
path: '/api/as/v0/engines/:engineName/crawler/domains/:domainId/entry_points/:entryPointId',
|
||||
params: {
|
||||
respond_with: 'index',
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
|
@ -10,6 +10,7 @@ import { RouteDependencies } from '../../plugin';
|
|||
import { registerAnalyticsRoutes } from './analytics';
|
||||
import { registerApiLogsRoutes } from './api_logs';
|
||||
import { registerCrawlerRoutes } from './crawler';
|
||||
import { registerCrawlerEntryPointRoutes } from './crawler_entry_points';
|
||||
import { registerCredentialsRoutes } from './credentials';
|
||||
import { registerCurationsRoutes } from './curations';
|
||||
import { registerDocumentsRoutes, registerDocumentRoutes } from './documents';
|
||||
|
@ -44,4 +45,5 @@ export const registerAppSearchRoutes = (dependencies: RouteDependencies) => {
|
|||
registerApiLogsRoutes(dependencies);
|
||||
registerOnboardingRoutes(dependencies);
|
||||
registerCrawlerRoutes(dependencies);
|
||||
registerCrawlerEntryPointRoutes(dependencies);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue