mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Enterprise Search] Prevents users from creating indices already managed elsewhere (#137108)
This commit is contained in:
parent
e3eaf4615a
commit
967ce6d4de
10 changed files with 192 additions and 80 deletions
|
@ -7,6 +7,7 @@
|
|||
|
||||
export enum ErrorCode {
|
||||
CONNECTOR_DOCUMENT_ALREADY_EXISTS = 'connector_document_already_exists',
|
||||
CRAWLER_ALREADY_EXISTS = 'crawler_already_exists',
|
||||
INDEX_ALREADY_EXISTS = 'index_already_exists',
|
||||
INDEX_NOT_FOUND = 'index_not_found',
|
||||
RESOURCE_NOT_FOUND = 'resource_not_found',
|
||||
|
|
|
@ -9,7 +9,6 @@ import { LogicMounter } from '../../../__mocks__/kea_logic';
|
|||
|
||||
import { nextTick } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { Status } from '../../../../../common/types/api';
|
||||
import { IndexExistsApiLogic } from '../../api/index/index_exists_api_logic';
|
||||
|
||||
import { UNIVERSAL_LANGUAGE_VALUE } from './constants';
|
||||
|
@ -20,11 +19,9 @@ const DEFAULT_VALUES: NewSearchIndexValues = {
|
|||
fullIndexName: 'search-',
|
||||
fullIndexNameExists: false,
|
||||
fullIndexNameIsValid: true,
|
||||
isLoading: false,
|
||||
language: null,
|
||||
languageSelectValue: UNIVERSAL_LANGUAGE_VALUE,
|
||||
rawName: '',
|
||||
status: Status.IDLE,
|
||||
};
|
||||
|
||||
describe('NewSearchIndexLogic', () => {
|
||||
|
@ -107,19 +104,7 @@ describe('NewSearchIndexLogic', () => {
|
|||
data: { exists: true, indexName: 'search-indexname' },
|
||||
fullIndexName: 'search-indexname',
|
||||
fullIndexNameExists: true,
|
||||
isLoading: false,
|
||||
rawName: 'indexname',
|
||||
status: Status.SUCCESS,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('isLoading', () => {
|
||||
it('should set to true when status is loading', () => {
|
||||
IndexExistsApiLogic.actions.makeRequest({ indexName: 'search-indexname' });
|
||||
expect(NewSearchIndexLogic.values).toEqual({
|
||||
...DEFAULT_VALUES,
|
||||
isLoading: true,
|
||||
status: Status.LOADING,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
import { kea, MakeLogicType } from 'kea';
|
||||
|
||||
import { Status } from '../../../../../common/types/api';
|
||||
|
||||
import { Actions } from '../../../shared/api_logic/create_api_logic';
|
||||
|
||||
import {
|
||||
|
@ -28,11 +26,9 @@ export interface NewSearchIndexValues {
|
|||
fullIndexName: string;
|
||||
fullIndexNameExists: boolean;
|
||||
fullIndexNameIsValid: boolean;
|
||||
isLoading: boolean;
|
||||
language: LanguageForOptimization;
|
||||
languageSelectValue: string;
|
||||
rawName: string;
|
||||
status: Status;
|
||||
}
|
||||
|
||||
export type NewSearchIndexActions = Pick<
|
||||
|
@ -50,7 +46,7 @@ export const NewSearchIndexLogic = kea<MakeLogicType<NewSearchIndexValues, NewSe
|
|||
},
|
||||
connect: {
|
||||
actions: [IndexExistsApiLogic, ['makeRequest']],
|
||||
values: [IndexExistsApiLogic, ['data', 'status']],
|
||||
values: [IndexExistsApiLogic, ['data']],
|
||||
},
|
||||
listeners: ({ actions, values }) => ({
|
||||
setRawName: async (_, breakpoint) => {
|
||||
|
@ -84,7 +80,6 @@ export const NewSearchIndexLogic = kea<MakeLogicType<NewSearchIndexValues, NewSe
|
|||
() => [selectors.fullIndexName],
|
||||
(fullIndexName) => isValidIndexName(fullIndexName),
|
||||
],
|
||||
isLoading: [() => [selectors.status], (status: Status) => status === Status.LOADING],
|
||||
language: [
|
||||
() => [selectors.languageSelectValue],
|
||||
(languageSelectValue) => getLanguageForOptimization(languageSelectValue),
|
||||
|
|
|
@ -57,7 +57,6 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
|
|||
fullIndexName,
|
||||
fullIndexNameExists,
|
||||
fullIndexNameIsValid,
|
||||
isLoading,
|
||||
language,
|
||||
rawName,
|
||||
languageSelectValue,
|
||||
|
@ -197,8 +196,8 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
isDisabled={!rawName || buttonLoading || isLoading || formInvalid}
|
||||
isLoading={buttonLoading || isLoading}
|
||||
isDisabled={!rawName || buttonLoading || formInvalid}
|
||||
isLoading={buttonLoading}
|
||||
type="submit"
|
||||
>
|
||||
{i18n.translate(
|
||||
|
|
|
@ -15,9 +15,10 @@ export const plugin = (initializerContext: PluginInitializerContext) => {
|
|||
};
|
||||
|
||||
export const configSchema = schema.object({
|
||||
host: schema.maybe(schema.string()),
|
||||
accessCheckTimeout: schema.number({ defaultValue: 5000 }),
|
||||
accessCheckTimeoutWarning: schema.number({ defaultValue: 300 }),
|
||||
customHeaders: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
host: schema.maybe(schema.string()),
|
||||
ssl: schema.object({
|
||||
certificateAuthorities: schema.maybe(
|
||||
schema.oneOf([schema.arrayOf(schema.string(), { minSize: 1 }), schema.string()])
|
||||
|
@ -27,17 +28,17 @@ export const configSchema = schema.object({
|
|||
{ defaultValue: 'full' }
|
||||
),
|
||||
}),
|
||||
customHeaders: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
});
|
||||
|
||||
export type ConfigType = TypeOf<typeof configSchema>;
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigType> = {
|
||||
schema: configSchema,
|
||||
exposeToBrowser: {
|
||||
host: true,
|
||||
},
|
||||
schema: configSchema,
|
||||
};
|
||||
export const CONNECTORS_INDEX = '.elastic-connectors';
|
||||
export const CONNECTORS_JOBS_INDEX = '.elastic-connectors-sync-jobs';
|
||||
export const CONNECTORS_VERSION = '1';
|
||||
export const CRAWLERS_INDEX = '.ent-search-actastic-crawler2_configurations';
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ErrorCode } from '../../../common/types/error_codes';
|
|||
|
||||
import { setupConnectorsIndices } from '../../index_management/setup_indices';
|
||||
|
||||
import { fetchCrawlerByIndexName } from '../crawler/fetch_crawlers';
|
||||
|
||||
import { addConnector } from './add_connector';
|
||||
import { fetchConnectorByIndexName } from './fetch_connectors';
|
||||
|
||||
|
@ -21,6 +23,7 @@ jest.mock('../../index_management/setup_indices', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('./fetch_connectors', () => ({ fetchConnectorByIndexName: jest.fn() }));
|
||||
jest.mock('../crawler/fetch_crawlers', () => ({ fetchCrawlerByIndexName: jest.fn() }));
|
||||
|
||||
describe('addConnector lib function', () => {
|
||||
const mockClient = {
|
||||
|
@ -44,6 +47,7 @@ describe('addConnector lib function', () => {
|
|||
mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' }));
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
|
@ -76,6 +80,7 @@ describe('addConnector lib function', () => {
|
|||
mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' }));
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => true);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
|
@ -90,6 +95,7 @@ describe('addConnector lib function', () => {
|
|||
mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' }));
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => true);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
|
@ -104,6 +110,7 @@ describe('addConnector lib function', () => {
|
|||
mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' }));
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => true);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => true);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
|
@ -118,6 +125,7 @@ describe('addConnector lib function', () => {
|
|||
mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' }));
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => ({ id: 'connectorId' }));
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
|
@ -160,6 +168,7 @@ describe('addConnector lib function', () => {
|
|||
});
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => false);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
index_name: 'search-index_name',
|
||||
|
@ -195,6 +204,7 @@ describe('addConnector lib function', () => {
|
|||
});
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => false);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => undefined);
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
index_name: 'index_name',
|
||||
|
@ -204,4 +214,20 @@ describe('addConnector lib function', () => {
|
|||
expect(setupConnectorsIndices).not.toHaveBeenCalled();
|
||||
expect(mockClient.asCurrentUser.index).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('should not create index if crawler exists', async () => {
|
||||
mockClient.asCurrentUser.index.mockImplementationOnce(() => {
|
||||
return 'connector ';
|
||||
});
|
||||
mockClient.asCurrentUser.indices.exists.mockImplementation(() => false);
|
||||
(fetchConnectorByIndexName as jest.Mock).mockImplementation(() => false);
|
||||
(fetchCrawlerByIndexName as jest.Mock).mockImplementation(() => 'crawler');
|
||||
await expect(
|
||||
addConnector(mockClient as unknown as IScopedClusterClient, {
|
||||
index_name: 'index_name',
|
||||
language: 'en',
|
||||
})
|
||||
).rejects.toEqual(new Error(ErrorCode.CRAWLER_ALREADY_EXISTS));
|
||||
expect(setupConnectorsIndices).not.toHaveBeenCalled();
|
||||
expect(mockClient.asCurrentUser.index).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ErrorCode } from '../../../common/types/error_codes';
|
|||
import { setupConnectorsIndices } from '../../index_management/setup_indices';
|
||||
import { isIndexNotFoundException } from '../../utils/identify_exceptions';
|
||||
|
||||
import { fetchCrawlerByIndexName } from '../crawler/fetch_crawlers';
|
||||
|
||||
import { deleteConnectorById } from './delete_connector';
|
||||
|
||||
import { fetchConnectorByIndexName } from './fetch_connectors';
|
||||
|
@ -39,6 +41,12 @@ const createConnector = async (
|
|||
throw new Error(ErrorCode.CONNECTOR_DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
const crawler = await fetchCrawlerByIndexName(client, index);
|
||||
|
||||
if (crawler) {
|
||||
throw new Error(ErrorCode.CRAWLER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
const result = await client.asCurrentUser.index({
|
||||
document,
|
||||
index: CONNECTORS_INDEX,
|
||||
|
|
|
@ -26,12 +26,6 @@ describe('crawler routes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('creates a request to enterprise search', () => {
|
||||
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
|
||||
path: '/api/ent/v1/internal/indices',
|
||||
});
|
||||
});
|
||||
|
||||
it('validates correctly with name and language', () => {
|
||||
const request = { body: { index_name: 'index-name', language: 'en' } };
|
||||
mockRouter.shouldValidate(request);
|
||||
|
@ -109,7 +103,7 @@ describe('crawler routes', () => {
|
|||
});
|
||||
|
||||
it('validates correctly with name and id', () => {
|
||||
const request = { params: { indexName: 'index-name', crawlRequestId: '12345' } };
|
||||
const request = { params: { crawlRequestId: '12345', indexName: 'index-name' } };
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
|
@ -153,46 +147,46 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly with domain urls', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { overrides: { domain_allowlist: ['https://www.elastic.co'] } },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with max crawl depth', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { overrides: { max_crawl_depth: 10 } },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with seed urls', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { overrides: { seed_urls: ['https://www.elastic.co/guide'] } },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with sitemap urls', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { overrides: { sitemap_urls: ['https://www.elastic.co/sitemap1.xml'] } },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly when we set sitemap discovery', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { overrides: { sitemap_discovery_disabled: true } },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with empty overrides', () => {
|
||||
const request = { params: { indexName: 'index-name' }, body: { overrides: {} } };
|
||||
const request = { body: { overrides: {} }, params: { indexName: 'index-name' } };
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
|
@ -298,24 +292,24 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly with params and body', () => {
|
||||
const request = {
|
||||
body: { entry_points: [{ value: '/guide' }], name: 'https://elastic.co/guide' },
|
||||
params: { indexName: 'index-name' },
|
||||
body: { name: 'https://elastic.co/guide', entry_points: [{ value: '/guide' }] },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails validation without a name param', () => {
|
||||
const request = {
|
||||
body: { entry_points: [{ value: '/guide' }], name: 'https://elastic.co/guide' },
|
||||
params: {},
|
||||
body: { name: 'https://elastic.co/guide', entry_points: [{ value: '/guide' }] },
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
||||
it('fails validation without a body', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: {},
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
@ -344,7 +338,7 @@ describe('crawler routes', () => {
|
|||
});
|
||||
|
||||
it('validates correctly with name and id', () => {
|
||||
const request = { params: { indexName: 'index-name', domainId: '1234' } };
|
||||
const request = { params: { domainId: '1234', indexName: 'index-name' } };
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
|
@ -383,35 +377,35 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly with crawl rules', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name', domainId: '1234' },
|
||||
body: {
|
||||
crawl_rules: [
|
||||
{
|
||||
order: 1,
|
||||
id: '5678',
|
||||
order: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
params: { domainId: '1234', indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with deduplication enabled', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name', domainId: '1234' },
|
||||
body: {
|
||||
deduplication_enabled: true,
|
||||
},
|
||||
params: { domainId: '1234', indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly with deduplication fields', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name', domainId: '1234' },
|
||||
body: {
|
||||
deduplication_fields: ['title', 'description'],
|
||||
},
|
||||
params: { domainId: '1234', indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
@ -440,7 +434,7 @@ describe('crawler routes', () => {
|
|||
});
|
||||
|
||||
it('validates correctly with name and id', () => {
|
||||
const request = { params: { indexName: 'index-name', domainId: '1234' } };
|
||||
const request = { params: { domainId: '1234', indexName: 'index-name' } };
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
|
@ -479,7 +473,7 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly with body', () => {
|
||||
const request = {
|
||||
body: { url: 'elastic.co', checks: ['tcp', 'url_request'] },
|
||||
body: { checks: ['tcp', 'url_request'], url: 'elastic.co' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
@ -516,24 +510,24 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { domains: ['https://elastic.co', 'https://swiftype.com'] },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('validates correctly without body', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: {},
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails validation without a name param', () => {
|
||||
const request = {
|
||||
params: {},
|
||||
body: { domains: ['https://elastic.co', 'https://swiftype.com'] },
|
||||
params: {},
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
@ -600,32 +594,32 @@ describe('crawler routes', () => {
|
|||
|
||||
it('validates correctly', () => {
|
||||
const request = {
|
||||
body: { frequency: 7, unit: 'day' },
|
||||
params: { indexName: 'index-name' },
|
||||
body: { unit: 'day', frequency: 7 },
|
||||
};
|
||||
mockRouter.shouldValidate(request);
|
||||
});
|
||||
|
||||
it('fails validation without a name param', () => {
|
||||
const request = {
|
||||
body: { frequency: 7, unit: 'day' },
|
||||
params: {},
|
||||
body: { unit: 'day', frequency: 7 },
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
||||
it('fails validation without a unit property in body', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { frequency: 7 },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
||||
it('fails validation without a frequency property in body', () => {
|
||||
const request = {
|
||||
params: { indexName: 'index-name' },
|
||||
body: { unit: 'day' },
|
||||
params: { indexName: 'index-name' },
|
||||
};
|
||||
mockRouter.shouldThrow(request);
|
||||
});
|
||||
|
|
|
@ -7,7 +7,14 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ErrorCode } from '../../../../common/types/error_codes';
|
||||
import { fetchConnectorByIndexName } from '../../../lib/connectors/fetch_connectors';
|
||||
import { fetchCrawlerByIndexName } from '../../../lib/crawler/fetch_crawlers';
|
||||
|
||||
import { RouteDependencies } from '../../../plugin';
|
||||
import { createError } from '../../../utils/create_error';
|
||||
|
||||
import { registerCrawlerCrawlRulesRoutes } from './crawler_crawl_rules';
|
||||
import { registerCrawlerEntryPointRoutes } from './crawler_entry_points';
|
||||
|
@ -26,9 +33,58 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
path: '/api/ent/v1/internal/indices',
|
||||
})
|
||||
async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const indexExists = await client.asCurrentUser.indices.exists({
|
||||
index: request.body.index_name,
|
||||
});
|
||||
if (indexExists) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.INDEX_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.addCrawler.indexExistsError',
|
||||
{
|
||||
defaultMessage: 'This index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
const crawler = await fetchCrawlerByIndexName(client, request.body.index_name);
|
||||
if (crawler) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.CRAWLER_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.addCrawler.crawlerExistsError',
|
||||
{
|
||||
defaultMessage: 'A crawler for this index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
|
||||
const connector = await fetchConnectorByIndexName(client, request.body.index_name);
|
||||
|
||||
if (connector) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.CONNECTOR_DOCUMENT_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.addCrawler.connectorExistsError',
|
||||
{
|
||||
defaultMessage: 'A connector for this index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
return enterpriseSearchRequestHandler.createRequest({
|
||||
path: '/api/ent/v1/internal/indices',
|
||||
})(context, request, response);
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
|
@ -36,8 +92,8 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
path: '/internal/enterprise_search/crawler/validate_url',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
url: schema.string(),
|
||||
checks: schema.arrayOf(schema.string()),
|
||||
url: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -64,20 +120,20 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
{
|
||||
path: '/internal/enterprise_search/indices/{indexName}/crawler/crawl_requests',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
overrides: schema.maybe(
|
||||
schema.object({
|
||||
domain_allowlist: schema.maybe(schema.arrayOf(schema.string())),
|
||||
max_crawl_depth: schema.maybe(schema.number()),
|
||||
seed_urls: schema.maybe(schema.arrayOf(schema.string())),
|
||||
sitemap_urls: schema.maybe(schema.arrayOf(schema.string())),
|
||||
sitemap_discovery_disabled: schema.maybe(schema.boolean()),
|
||||
sitemap_urls: schema.maybe(schema.arrayOf(schema.string())),
|
||||
})
|
||||
),
|
||||
}),
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
|
@ -104,8 +160,8 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
path: '/internal/enterprise_search/indices/{indexName}/crawler/crawl_requests/{crawlRequestId}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
crawlRequestId: schema.string(),
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -173,22 +229,22 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
{
|
||||
path: '/internal/enterprise_search/indices/{indexName}/crawler/domains/{domainId}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
domainId: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
crawl_rules: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
order: schema.number(),
|
||||
id: schema.string(),
|
||||
order: schema.number(),
|
||||
})
|
||||
)
|
||||
),
|
||||
deduplication_enabled: schema.maybe(schema.boolean()),
|
||||
deduplication_fields: schema.maybe(schema.arrayOf(schema.string())),
|
||||
}),
|
||||
params: schema.object({
|
||||
domainId: schema.string(),
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
|
@ -229,12 +285,12 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
{
|
||||
path: '/internal/enterprise_search/indices/{indexName}/crawler/process_crawls',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
domains: schema.maybe(schema.arrayOf(schema.string())),
|
||||
}),
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
|
@ -260,13 +316,13 @@ export function registerCrawlerRoutes(routeDependencies: RouteDependencies) {
|
|||
{
|
||||
path: '/internal/enterprise_search/indices/{indexName}/crawler/crawl_schedule',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
frequency: schema.number(),
|
||||
unit: schema.string(),
|
||||
}),
|
||||
params: schema.object({
|
||||
indexName: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
unit: schema.string(),
|
||||
frequency: schema.number(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
enterpriseSearchRequestHandler.createRequest({
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ErrorCode } from '../../../common/types/error_codes';
|
||||
|
||||
import { fetchConnectors } from '../../lib/connectors/fetch_connectors';
|
||||
import { fetchCrawlers } from '../../lib/crawler/fetch_crawlers';
|
||||
import { fetchConnectorByIndexName, fetchConnectors } from '../../lib/connectors/fetch_connectors';
|
||||
import { fetchCrawlerByIndexName, fetchCrawlers } from '../../lib/crawler/fetch_crawlers';
|
||||
|
||||
import { createApiIndex } from '../../lib/indices/create_index';
|
||||
import { fetchIndex } from '../../lib/indices/fetch_index';
|
||||
|
@ -199,6 +200,52 @@ export function registerIndexRoutes({ router }: RouteDependencies) {
|
|||
const { ['index_name']: indexName, language } = request.body;
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
try {
|
||||
const indexExists = await client.asCurrentUser.indices.exists({
|
||||
index: request.body.index_name,
|
||||
});
|
||||
if (indexExists) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.INDEX_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.createApiIndex.indexExistsError',
|
||||
{
|
||||
defaultMessage: 'This index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
const crawler = await fetchCrawlerByIndexName(client, request.body.index_name);
|
||||
if (crawler) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.CRAWLER_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.createApiIndex.crawlerExistsError',
|
||||
{
|
||||
defaultMessage: 'A crawler for this index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
|
||||
const connector = await fetchConnectorByIndexName(client, request.body.index_name);
|
||||
|
||||
if (connector) {
|
||||
return createError({
|
||||
errorCode: ErrorCode.CONNECTOR_DOCUMENT_ALREADY_EXISTS,
|
||||
message: i18n.translate(
|
||||
'xpack.enterpriseSearch.server.routes.createApiIndex.connectorExistsError',
|
||||
{
|
||||
defaultMessage: 'A connector for this index already exists',
|
||||
}
|
||||
),
|
||||
response,
|
||||
statusCode: 409,
|
||||
});
|
||||
}
|
||||
const createIndexResponse = await createApiIndex(client, indexName, language);
|
||||
return response.ok({
|
||||
body: createIndexResponse,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue