mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[EEM] Migrate to using @kbn/server-route-repository (#191102)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8a429b5953
commit
b9319a6ad4
23 changed files with 612 additions and 571 deletions
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
type Method = 'get' | 'post' | 'put' | 'delete';
|
||||
type Method = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
||||
|
||||
export function parseEndpoint(endpoint: string) {
|
||||
const parts = endpoint.split(' ');
|
||||
|
@ -15,7 +15,7 @@ export function parseEndpoint(endpoint: string) {
|
|||
const pathname = parts[1].trim();
|
||||
const version = parts[2]?.trim();
|
||||
|
||||
if (!['get', 'post', 'put', 'delete'].includes(method)) {
|
||||
if (!['get', 'post', 'put', 'patch', 'delete'].includes(method)) {
|
||||
throw new Error(`Endpoint ${endpoint} was not prefixed with a valid HTTP method`);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,12 @@ components:
|
|||
type: object
|
||||
properties:
|
||||
installOnly:
|
||||
type: boolean
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- "true"
|
||||
- "false"
|
||||
- type: boolean
|
||||
default: false
|
||||
additionalProperties: false
|
||||
getEntityDefinitionQuerySchema:
|
||||
|
@ -39,7 +44,12 @@ components:
|
|||
type: object
|
||||
properties:
|
||||
deleteData:
|
||||
type: boolean
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- "true"
|
||||
- "false"
|
||||
- type: boolean
|
||||
default: false
|
||||
additionalProperties: false
|
||||
entityDefinitionSchema:
|
||||
|
@ -393,6 +403,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: enabled
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
|
@ -422,6 +433,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: success
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
|
@ -447,6 +459,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: success
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
|
@ -491,7 +504,8 @@ paths:
|
|||
$ref: "#/components/schemas/entityDefinitionSchema"
|
||||
"400":
|
||||
description: The entity definition cannot be installed; see the error for more
|
||||
details
|
||||
details but commonly due to validation failures of the definition ID
|
||||
or metrics format
|
||||
"409":
|
||||
description: An entity definition with this ID already exists
|
||||
delete:
|
||||
|
@ -564,4 +578,39 @@ paths:
|
|||
type: boolean
|
||||
running:
|
||||
type: boolean
|
||||
patch:
|
||||
description: Update an entity definition.
|
||||
tags:
|
||||
- definitions
|
||||
parameters:
|
||||
- in: query
|
||||
name: installOnly
|
||||
description: If true, the definition transforms will not be started
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
requestBody:
|
||||
description: The definition properties to update
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/entityDefinitionUpdateSchema"
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/entityDefinitionSchema"
|
||||
"400":
|
||||
description: The entity definition cannot be installed; see the error for more
|
||||
details
|
||||
"403":
|
||||
description: User is not allowed to update the entity definition
|
||||
"404":
|
||||
description: The entity definition does not exist
|
||||
"409":
|
||||
description: The entity definition is being updated by another request
|
||||
tags: []
|
||||
|
|
|
@ -11,5 +11,3 @@ import { BooleanFromString } from '@kbn/zod-helpers';
|
|||
export const createEntityDefinitionQuerySchema = z.object({
|
||||
installOnly: z.optional(BooleanFromString).default(false),
|
||||
});
|
||||
|
||||
export type CreateEntityDefinitionQuery = z.infer<typeof createEntityDefinitionQuerySchema>;
|
||||
|
|
|
@ -15,5 +15,3 @@ export const deleteEntityDefinitionParamsSchema = z.object({
|
|||
export const deleteEntityDefinitionQuerySchema = z.object({
|
||||
deleteData: z.optional(BooleanFromString).default(false),
|
||||
});
|
||||
|
||||
export type DeleteEntityDefinitionQuery = z.infer<typeof deleteEntityDefinitionQuerySchema>;
|
||||
|
|
|
@ -5,32 +5,60 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { HttpStart } from '@kbn/core/public';
|
||||
import { CreateEntityDefinitionQuery, DeleteEntityDefinitionQuery } from '@kbn/entities-schema';
|
||||
import { EntityManagerUnauthorizedError } from './errors';
|
||||
import { IEntityClient } from '../types';
|
||||
import { CoreSetup, CoreStart } from '@kbn/core/public';
|
||||
import {
|
||||
ClientRequestParamsOf,
|
||||
RouteRepositoryClient,
|
||||
createRepositoryClient,
|
||||
isHttpFetchError,
|
||||
} from '@kbn/server-route-repository-client';
|
||||
import {
|
||||
ManagedEntityEnabledResponse,
|
||||
EnableManagedEntityResponse,
|
||||
DisableManagedEntityResponse,
|
||||
EnableManagedEntityResponse,
|
||||
ManagedEntityEnabledResponse,
|
||||
} from '../../common/types_api';
|
||||
import type { EntityManagerRouteRepository } from '../../server';
|
||||
import { EntityManagerUnauthorizedError } from './errors';
|
||||
|
||||
export class EntityClient implements IEntityClient {
|
||||
constructor(private readonly http: HttpStart) {}
|
||||
type EntityManagerRepositoryClient = RouteRepositoryClient<EntityManagerRouteRepository>;
|
||||
|
||||
type QueryParamOf<T extends { params?: any }> = Exclude<T['params'], undefined>['query'];
|
||||
|
||||
type DeleteEntityDefinitionQuery = QueryParamOf<
|
||||
ClientRequestParamsOf<
|
||||
EntityManagerRouteRepository,
|
||||
'DELETE /internal/entities/managed/enablement'
|
||||
>
|
||||
>;
|
||||
|
||||
type CreateEntityDefinitionQuery = QueryParamOf<
|
||||
ClientRequestParamsOf<EntityManagerRouteRepository, 'PUT /internal/entities/managed/enablement'>
|
||||
>;
|
||||
|
||||
export class EntityClient {
|
||||
public readonly repositoryClient: EntityManagerRepositoryClient;
|
||||
|
||||
constructor(core: CoreStart | CoreSetup) {
|
||||
this.repositoryClient = createRepositoryClient<EntityManagerRouteRepository>(core).fetch;
|
||||
}
|
||||
|
||||
async isManagedEntityDiscoveryEnabled(): Promise<ManagedEntityEnabledResponse> {
|
||||
return await this.http.get('/internal/entities/managed/enablement');
|
||||
return await this.repositoryClient('GET /internal/entities/managed/enablement');
|
||||
}
|
||||
|
||||
async enableManagedEntityDiscovery(
|
||||
query?: CreateEntityDefinitionQuery
|
||||
): Promise<EnableManagedEntityResponse> {
|
||||
try {
|
||||
return await this.http.put('/internal/entities/managed/enablement', {
|
||||
query,
|
||||
return await this.repositoryClient('PUT /internal/entities/managed/enablement', {
|
||||
params: {
|
||||
query: {
|
||||
installOnly: query?.installOnly,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.body?.statusCode === 403) {
|
||||
if (isHttpFetchError(err) && err.body?.statusCode === 403) {
|
||||
throw new EntityManagerUnauthorizedError(err.body.message);
|
||||
}
|
||||
throw err;
|
||||
|
@ -41,9 +69,15 @@ export class EntityClient implements IEntityClient {
|
|||
query?: DeleteEntityDefinitionQuery
|
||||
): Promise<DisableManagedEntityResponse> {
|
||||
try {
|
||||
return await this.http.delete('/internal/entities/managed/enablement', { query });
|
||||
return await this.repositoryClient('DELETE /internal/entities/managed/enablement', {
|
||||
params: {
|
||||
query: {
|
||||
deleteData: query?.deleteData,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.body?.statusCode === 403) {
|
||||
if (isHttpFetchError(err) && err.body?.statusCode === 403) {
|
||||
throw new EntityManagerUnauthorizedError(err.body.message);
|
||||
}
|
||||
throw err;
|
||||
|
|
|
@ -22,14 +22,14 @@ export class Plugin implements EntityManagerPluginClass {
|
|||
}
|
||||
|
||||
setup(core: CoreSetup) {
|
||||
const entityClient = new EntityClient(core.http);
|
||||
const entityClient = new EntityClient(core);
|
||||
return {
|
||||
entityClient,
|
||||
};
|
||||
}
|
||||
|
||||
start(core: CoreStart) {
|
||||
const entityClient = new EntityClient(core.http);
|
||||
const entityClient = new EntityClient(core);
|
||||
return {
|
||||
entityClient,
|
||||
};
|
||||
|
|
|
@ -5,30 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type { Plugin as PluginClass } from '@kbn/core/public';
|
||||
import { CreateEntityDefinitionQuery } from '@kbn/entities-schema';
|
||||
import {
|
||||
DisableManagedEntityResponse,
|
||||
EnableManagedEntityResponse,
|
||||
ManagedEntityEnabledResponse,
|
||||
} from '../common/types_api';
|
||||
import type { EntityClient } from './lib/entity_client';
|
||||
|
||||
export interface EntityManagerPublicPluginSetup {
|
||||
entityClient: IEntityClient;
|
||||
entityClient: EntityClient;
|
||||
}
|
||||
|
||||
export interface EntityManagerPublicPluginStart {
|
||||
entityClient: IEntityClient;
|
||||
entityClient: EntityClient;
|
||||
}
|
||||
|
||||
export type EntityManagerPluginClass = PluginClass<
|
||||
EntityManagerPublicPluginSetup | undefined,
|
||||
EntityManagerPublicPluginStart | undefined
|
||||
EntityManagerPublicPluginSetup,
|
||||
EntityManagerPublicPluginStart
|
||||
>;
|
||||
|
||||
export interface IEntityClient {
|
||||
isManagedEntityDiscoveryEnabled: () => Promise<ManagedEntityEnabledResponse>;
|
||||
enableManagedEntityDiscovery: (
|
||||
query?: CreateEntityDefinitionQuery
|
||||
) => Promise<EnableManagedEntityResponse>;
|
||||
disableManagedEntityDiscovery: () => Promise<DisableManagedEntityResponse>;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,14 @@
|
|||
import { PluginInitializerContext } from '@kbn/core-plugins-server';
|
||||
import { EntityManagerConfig } from '../common/config';
|
||||
import { EntityManagerServerPluginSetup, EntityManagerServerPluginStart, config } from './plugin';
|
||||
import { EntityManagerRouteRepository } from './routes';
|
||||
|
||||
export type { EntityManagerConfig, EntityManagerServerPluginSetup, EntityManagerServerPluginStart };
|
||||
export type {
|
||||
EntityManagerConfig,
|
||||
EntityManagerServerPluginSetup,
|
||||
EntityManagerServerPluginStart,
|
||||
EntityManagerRouteRepository,
|
||||
};
|
||||
export { config };
|
||||
|
||||
export const plugin = async (context: PluginInitializerContext<EntityManagerConfig>) => {
|
||||
|
|
|
@ -5,29 +5,30 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import {
|
||||
Plugin,
|
||||
CoreSetup,
|
||||
RequestHandlerContext,
|
||||
CoreStart,
|
||||
PluginInitializerContext,
|
||||
PluginConfigDescriptor,
|
||||
Logger,
|
||||
KibanaRequest,
|
||||
Logger,
|
||||
Plugin,
|
||||
PluginConfigDescriptor,
|
||||
PluginInitializerContext,
|
||||
} from '@kbn/core/server';
|
||||
import { registerRoutes } from '@kbn/server-route-repository';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { EntityManagerConfig, configSchema, exposeToBrowserConfig } from '../common/config';
|
||||
import { builtInDefinitions } from './lib/entities/built_in';
|
||||
import { upgradeBuiltInEntityDefinitions } from './lib/entities/upgrade_entity_definition';
|
||||
import { EntityClient } from './lib/entity_client';
|
||||
import { installEntityManagerTemplates } from './lib/manage_index_templates';
|
||||
import { setupRoutes } from './routes';
|
||||
import { entityManagerRouteRepository } from './routes';
|
||||
import { EntityManagerRouteDependencies } from './routes/types';
|
||||
import { EntityDiscoveryApiKeyType, entityDefinition } from './saved_objects';
|
||||
import {
|
||||
EntityManagerPluginSetupDependencies,
|
||||
EntityManagerPluginStartDependencies,
|
||||
EntityManagerServerSetup,
|
||||
} from './types';
|
||||
import { EntityManagerConfig, configSchema, exposeToBrowserConfig } from '../common/config';
|
||||
import { entityDefinition, EntityDiscoveryApiKeyType } from './saved_objects';
|
||||
import { upgradeBuiltInEntityDefinitions } from './lib/entities/upgrade_entity_definition';
|
||||
import { builtInDefinitions } from './lib/entities/built_in';
|
||||
import { EntityClient } from './lib/entity_client';
|
||||
|
||||
export type EntityManagerServerPluginSetup = ReturnType<EntityManagerServerPlugin['setup']>;
|
||||
export type EntityManagerServerPluginStart = ReturnType<EntityManagerServerPlugin['start']>;
|
||||
|
@ -64,23 +65,24 @@ export class EntityManagerServerPlugin
|
|||
attributesToIncludeInAAD: new Set(['id', 'name']),
|
||||
});
|
||||
|
||||
const router = core.http.createRouter();
|
||||
|
||||
this.server = {
|
||||
config: this.config,
|
||||
logger: this.logger,
|
||||
} as EntityManagerServerSetup;
|
||||
|
||||
setupRoutes<RequestHandlerContext>({
|
||||
router,
|
||||
logger: this.logger,
|
||||
server: this.server,
|
||||
getScopedClient: async ({ request }: { request: KibanaRequest }) => {
|
||||
const [coreStart] = await core.getStartServices();
|
||||
const esClient = coreStart.elasticsearch.client.asScoped(request).asCurrentUser;
|
||||
const soClient = coreStart.savedObjects.getScopedClient(request);
|
||||
return new EntityClient({ esClient, soClient, logger: this.logger });
|
||||
registerRoutes<EntityManagerRouteDependencies>({
|
||||
repository: entityManagerRouteRepository,
|
||||
dependencies: {
|
||||
server: this.server,
|
||||
getScopedClient: async ({ request }: { request: KibanaRequest }) => {
|
||||
const [coreStart] = await core.getStartServices();
|
||||
const esClient = coreStart.elasticsearch.client.asScoped(request).asCurrentUser;
|
||||
const soClient = coreStart.savedObjects.getScopedClient(request);
|
||||
return new EntityClient({ esClient, soClient, logger: this.logger });
|
||||
},
|
||||
},
|
||||
core,
|
||||
logger: this.logger,
|
||||
});
|
||||
|
||||
return {};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { createServerRouteFactory } from '@kbn/server-route-repository';
|
||||
import { EntityManagerRouteHandlerResources } from './types';
|
||||
|
||||
export const createEntityManagerServerRoute =
|
||||
createServerRouteFactory<EntityManagerRouteHandlerResources>();
|
|
@ -6,9 +6,6 @@
|
|||
*/
|
||||
|
||||
import semver from 'semver';
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { checkIfEntityDiscoveryAPIKeyIsValid, readEntityDiscoveryAPIKey } from '../../lib/auth';
|
||||
import {
|
||||
ERROR_API_KEY_NOT_FOUND,
|
||||
ERROR_API_KEY_NOT_VALID,
|
||||
|
@ -16,9 +13,11 @@ import {
|
|||
ERROR_DEFINITION_STOPPED,
|
||||
ERROR_PARTIAL_BUILTIN_INSTALLATION,
|
||||
} from '../../../common/errors';
|
||||
import { findEntityDefinitions } from '../../lib/entities/find_entity_definition';
|
||||
import { checkIfEntityDiscoveryAPIKeyIsValid, readEntityDiscoveryAPIKey } from '../../lib/auth';
|
||||
import { builtInDefinitions } from '../../lib/entities/built_in';
|
||||
import { findEntityDefinitions } from '../../lib/entities/find_entity_definition';
|
||||
import { getClientsFromAPIKey } from '../../lib/utils';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -34,6 +33,7 @@ import { getClientsFromAPIKey } from '../../lib/utils';
|
|||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: enabled
|
||||
* properties:
|
||||
* enabled:
|
||||
* type: boolean
|
||||
|
@ -42,77 +42,70 @@ import { getClientsFromAPIKey } from '../../lib/utils';
|
|||
* type: string
|
||||
* example: api_key_not_found
|
||||
*/
|
||||
export function checkEntityDiscoveryEnabledRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
server,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.get<unknown, unknown, unknown>(
|
||||
{
|
||||
path: '/internal/entities/managed/enablement',
|
||||
validate: false,
|
||||
},
|
||||
async (context, req, res) => {
|
||||
try {
|
||||
logger.debug('reading entity discovery API key from saved object');
|
||||
const apiKey = await readEntityDiscoveryAPIKey(server);
|
||||
export const checkEntityDiscoveryEnabledRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'GET /internal/entities/managed/enablement',
|
||||
handler: async ({ response, logger, server }) => {
|
||||
try {
|
||||
logger.debug('reading entity discovery API key from saved object');
|
||||
const apiKey = await readEntityDiscoveryAPIKey(server);
|
||||
|
||||
if (apiKey === undefined) {
|
||||
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_FOUND } });
|
||||
}
|
||||
|
||||
logger.debug('validating existing entity discovery API key');
|
||||
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);
|
||||
|
||||
if (!isValid) {
|
||||
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_VALID } });
|
||||
}
|
||||
|
||||
const { esClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
|
||||
const entityDiscoveryState = await Promise.all(
|
||||
builtInDefinitions.map(async (builtInDefinition) => {
|
||||
const definitions = await findEntityDefinitions({
|
||||
esClient,
|
||||
soClient,
|
||||
id: builtInDefinition.id,
|
||||
});
|
||||
|
||||
return { installedDefinition: definitions[0], builtInDefinition };
|
||||
})
|
||||
).then((results) =>
|
||||
results.reduce(
|
||||
(state, { installedDefinition, builtInDefinition }) => {
|
||||
return {
|
||||
installed: Boolean(state.installed && installedDefinition?.state.installed),
|
||||
running: Boolean(state.running && installedDefinition?.state.running),
|
||||
outdated:
|
||||
state.outdated ||
|
||||
(installedDefinition &&
|
||||
semver.neq(installedDefinition.version, builtInDefinition.version)),
|
||||
};
|
||||
},
|
||||
{ installed: true, running: true, outdated: false }
|
||||
)
|
||||
);
|
||||
|
||||
if (!entityDiscoveryState.installed) {
|
||||
return res.ok({ body: { enabled: false, reason: ERROR_PARTIAL_BUILTIN_INSTALLATION } });
|
||||
}
|
||||
|
||||
if (!entityDiscoveryState.running) {
|
||||
return res.ok({ body: { enabled: false, reason: ERROR_DEFINITION_STOPPED } });
|
||||
}
|
||||
|
||||
if (entityDiscoveryState.outdated) {
|
||||
return res.ok({ body: { enabled: false, reason: ERROR_BUILTIN_UPGRADE_REQUIRED } });
|
||||
}
|
||||
|
||||
return res.ok({ body: { enabled: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.customError({ statusCode: 500, body: err });
|
||||
if (apiKey === undefined) {
|
||||
return response.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_FOUND } });
|
||||
}
|
||||
|
||||
logger.debug('validating existing entity discovery API key');
|
||||
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);
|
||||
|
||||
if (!isValid) {
|
||||
return response.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_VALID } });
|
||||
}
|
||||
|
||||
const { esClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
|
||||
const entityDiscoveryState = await Promise.all(
|
||||
builtInDefinitions.map(async (builtInDefinition) => {
|
||||
const definitions = await findEntityDefinitions({
|
||||
esClient,
|
||||
soClient,
|
||||
id: builtInDefinition.id,
|
||||
});
|
||||
|
||||
return { installedDefinition: definitions[0], builtInDefinition };
|
||||
})
|
||||
).then((results) =>
|
||||
results.reduce(
|
||||
(state, { installedDefinition, builtInDefinition }) => {
|
||||
return {
|
||||
installed: Boolean(state.installed && installedDefinition?.state.installed),
|
||||
running: Boolean(state.running && installedDefinition?.state.running),
|
||||
outdated:
|
||||
state.outdated ||
|
||||
(installedDefinition &&
|
||||
semver.neq(installedDefinition.version, builtInDefinition.version)),
|
||||
};
|
||||
},
|
||||
{ installed: true, running: true, outdated: false }
|
||||
)
|
||||
);
|
||||
|
||||
if (!entityDiscoveryState.installed) {
|
||||
return response.ok({
|
||||
body: { enabled: false, reason: ERROR_PARTIAL_BUILTIN_INSTALLATION },
|
||||
});
|
||||
}
|
||||
|
||||
if (!entityDiscoveryState.running) {
|
||||
return response.ok({ body: { enabled: false, reason: ERROR_DEFINITION_STOPPED } });
|
||||
}
|
||||
|
||||
if (entityDiscoveryState.outdated) {
|
||||
return response.ok({ body: { enabled: false, reason: ERROR_BUILTIN_UPGRADE_REQUIRED } });
|
||||
}
|
||||
|
||||
return response.ok({ body: { enabled: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return response.customError({ statusCode: 500, body: err });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { z } from '@kbn/zod';
|
||||
import { BooleanFromString } from '@kbn/zod-helpers';
|
||||
import { deleteEntityDiscoveryAPIKey, readEntityDiscoveryAPIKey } from '../../lib/auth';
|
||||
import { uninstallBuiltInEntityDefinitions } from '../../lib/entities/uninstall_entity_definition';
|
||||
import { canDisableEntityDiscovery } from '../../lib/auth/privileges';
|
||||
import { uninstallBuiltInEntityDefinitions } from '../../lib/entities/uninstall_entity_definition';
|
||||
import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -29,72 +29,65 @@ import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
|||
* type: boolean
|
||||
* default: false
|
||||
* responses:
|
||||
* 403:
|
||||
* description: The current user does not have the required permissions to disable entity discovery
|
||||
* 200:
|
||||
* description: Built-in entity discovery successfully disabled
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: success
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* 403:
|
||||
* description: The current user does not have the required permissions to disable entity discovery
|
||||
*/
|
||||
export function disableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
server,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.delete<unknown, { deleteData?: boolean }, unknown>(
|
||||
{
|
||||
path: '/internal/entities/managed/enablement',
|
||||
validate: {
|
||||
query: schema.object({
|
||||
deleteData: schema.maybe(schema.boolean({ defaultValue: false })),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canDisable = await canDisableEntityDiscovery(esClient);
|
||||
if (!canDisable) {
|
||||
return res.forbidden({
|
||||
body: {
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to disable entity discovery',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const soClient = (await context.core).savedObjects.getClient({
|
||||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
export const disableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'DELETE /internal/entities/managed/enablement',
|
||||
params: z.object({
|
||||
query: z.object({
|
||||
deleteData: z.optional(BooleanFromString).default(false),
|
||||
}),
|
||||
}),
|
||||
handler: async ({ context, response, params, logger, server }) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canDisable = await canDisableEntityDiscovery(esClient);
|
||||
if (!canDisable) {
|
||||
return response.forbidden({
|
||||
body: {
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to disable entity discovery',
|
||||
},
|
||||
});
|
||||
|
||||
await uninstallBuiltInEntityDefinitions({
|
||||
soClient,
|
||||
esClient,
|
||||
logger,
|
||||
deleteData: req.query.deleteData,
|
||||
});
|
||||
|
||||
server.logger.debug('reading entity discovery API key from saved object');
|
||||
const apiKey = await readEntityDiscoveryAPIKey(server);
|
||||
// api key could be deleted outside of the apis, it does not affect the
|
||||
// disablement flow
|
||||
if (apiKey) {
|
||||
await deleteEntityDiscoveryAPIKey(soClient);
|
||||
await server.security.authc.apiKeys.invalidateAsInternalUser({
|
||||
ids: [apiKey.id],
|
||||
});
|
||||
}
|
||||
|
||||
return res.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.customError({ statusCode: 500, body: err });
|
||||
}
|
||||
|
||||
const soClient = (await context.core).savedObjects.getClient({
|
||||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
});
|
||||
|
||||
await uninstallBuiltInEntityDefinitions({
|
||||
soClient,
|
||||
esClient,
|
||||
logger,
|
||||
deleteData: params.query.deleteData,
|
||||
});
|
||||
|
||||
server.logger.debug('reading entity discovery API key from saved object');
|
||||
const apiKey = await readEntityDiscoveryAPIKey(server);
|
||||
// api key could be deleted outside of the apis, it does not affect the
|
||||
// disablement flow
|
||||
if (apiKey) {
|
||||
await deleteEntityDiscoveryAPIKey(soClient);
|
||||
await server.security.authc.apiKeys.invalidateAsInternalUser({
|
||||
ids: [apiKey.id],
|
||||
});
|
||||
}
|
||||
|
||||
return response.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return response.customError({ statusCode: 500, body: err });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import {
|
||||
CreateEntityDefinitionQuery,
|
||||
createEntityDefinitionQuerySchema,
|
||||
} from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { createEntityDefinitionQuerySchema } from '@kbn/entities-schema';
|
||||
import { z } from '@kbn/zod';
|
||||
import { ERROR_API_KEY_SERVICE_DISABLED } from '../../../common/errors';
|
||||
import {
|
||||
canEnableEntityDiscovery,
|
||||
checkIfAPIKeysAreEnabled,
|
||||
|
@ -22,9 +19,9 @@ import {
|
|||
} from '../../lib/auth';
|
||||
import { builtInDefinitions } from '../../lib/entities/built_in';
|
||||
import { installBuiltInEntityDefinitions } from '../../lib/entities/install_entity_definition';
|
||||
import { ERROR_API_KEY_SERVICE_DISABLED } from '../../../common/errors';
|
||||
import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
||||
import { startTransform } from '../../lib/entities/start_transform';
|
||||
import { EntityDiscoveryApiKeyType } from '../../saved_objects';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -42,14 +39,13 @@ import { startTransform } from '../../lib/entities/start_transform';
|
|||
* type: boolean
|
||||
* default: false
|
||||
* responses:
|
||||
* 403:
|
||||
* description: The current user does not have the required permissions to enable entity discovery
|
||||
* 200:
|
||||
* description: OK - Verify result in response body
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: success
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
|
@ -60,90 +56,84 @@ import { startTransform } from '../../lib/entities/start_transform';
|
|||
* message:
|
||||
* type: string
|
||||
* example: API key service is not enabled; try configuring `xpack.security.authc.api_key.enabled` in your elasticsearch config
|
||||
* 403:
|
||||
* description: The current user does not have the required permissions to enable entity discovery
|
||||
*/
|
||||
export function enableEntityDiscoveryRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
server,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.put<unknown, CreateEntityDefinitionQuery, unknown>(
|
||||
{
|
||||
path: '/internal/entities/managed/enablement',
|
||||
validate: {
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
try {
|
||||
const apiKeysEnabled = await checkIfAPIKeysAreEnabled(server);
|
||||
if (!apiKeysEnabled) {
|
||||
return res.ok({
|
||||
body: {
|
||||
success: false,
|
||||
reason: ERROR_API_KEY_SERVICE_DISABLED,
|
||||
message:
|
||||
'API key service is not enabled; try configuring `xpack.security.authc.api_key.enabled` in your elasticsearch config',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canEnable = await canEnableEntityDiscovery(esClient);
|
||||
if (!canEnable) {
|
||||
return res.forbidden({
|
||||
body: {
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to enable entity discovery',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const soClient = (await context.core).savedObjects.getClient({
|
||||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
export const enableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'PUT /internal/entities/managed/enablement',
|
||||
params: z.object({
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
}),
|
||||
handler: async ({ context, request, response, params, server, logger }) => {
|
||||
try {
|
||||
const apiKeysEnabled = await checkIfAPIKeysAreEnabled(server);
|
||||
if (!apiKeysEnabled) {
|
||||
return response.ok({
|
||||
body: {
|
||||
success: false,
|
||||
reason: ERROR_API_KEY_SERVICE_DISABLED,
|
||||
message:
|
||||
'API key service is not enabled; try configuring `xpack.security.authc.api_key.enabled` in your elasticsearch config',
|
||||
},
|
||||
});
|
||||
const existingApiKey = await readEntityDiscoveryAPIKey(server);
|
||||
|
||||
if (existingApiKey !== undefined) {
|
||||
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, existingApiKey);
|
||||
|
||||
if (!isValid) {
|
||||
await deleteEntityDiscoveryAPIKey(soClient);
|
||||
await server.security.authc.apiKeys.invalidateAsInternalUser({
|
||||
ids: [existingApiKey.id],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const apiKey = await generateEntityDiscoveryAPIKey(server, req);
|
||||
if (apiKey === undefined) {
|
||||
return res.customError({
|
||||
statusCode: 500,
|
||||
body: new Error('could not generate entity discovery API key'),
|
||||
});
|
||||
}
|
||||
|
||||
await saveEntityDiscoveryAPIKey(soClient, apiKey);
|
||||
|
||||
const installedDefinitions = await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
soClient,
|
||||
logger,
|
||||
definitions: builtInDefinitions,
|
||||
});
|
||||
|
||||
if (!req.query.installOnly) {
|
||||
await Promise.all(
|
||||
installedDefinitions.map((installedDefinition) =>
|
||||
startTransform(esClient, installedDefinition, logger)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.customError({ statusCode: 500, body: err });
|
||||
}
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const canEnable = await canEnableEntityDiscovery(esClient);
|
||||
if (!canEnable) {
|
||||
return response.forbidden({
|
||||
body: {
|
||||
message:
|
||||
'Current Kibana user does not have the required permissions to enable entity discovery',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const soClient = (await context.core).savedObjects.getClient({
|
||||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
});
|
||||
const existingApiKey = await readEntityDiscoveryAPIKey(server);
|
||||
|
||||
if (existingApiKey !== undefined) {
|
||||
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, existingApiKey);
|
||||
|
||||
if (!isValid) {
|
||||
await deleteEntityDiscoveryAPIKey(soClient);
|
||||
await server.security.authc.apiKeys.invalidateAsInternalUser({
|
||||
ids: [existingApiKey.id],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const apiKey = await generateEntityDiscoveryAPIKey(server, request);
|
||||
if (apiKey === undefined) {
|
||||
return response.customError({
|
||||
statusCode: 500,
|
||||
body: new Error('could not generate entity discovery API key'),
|
||||
});
|
||||
}
|
||||
|
||||
await saveEntityDiscoveryAPIKey(soClient, apiKey);
|
||||
|
||||
const installedDefinitions = await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
soClient,
|
||||
logger,
|
||||
definitions: builtInDefinitions,
|
||||
});
|
||||
|
||||
if (!params.query.installOnly) {
|
||||
await Promise.all(
|
||||
installedDefinitions.map((installedDefinition) =>
|
||||
startTransform(esClient, installedDefinition, logger)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return response.customError({ statusCode: 500, body: err });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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 { checkEntityDiscoveryEnabledRoute } from './check';
|
||||
import { enableEntityDiscoveryRoute } from './enable';
|
||||
import { disableEntityDiscoveryRoute } from './disable';
|
||||
|
||||
export const enablementRoutes = {
|
||||
...checkEntityDiscoveryEnabledRoute,
|
||||
...enableEntityDiscoveryRoute,
|
||||
...disableEntityDiscoveryRoute,
|
||||
};
|
|
@ -5,18 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import {
|
||||
EntityDefinition,
|
||||
entityDefinitionSchema,
|
||||
createEntityDefinitionQuerySchema,
|
||||
CreateEntityDefinitionQuery,
|
||||
} from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { createEntityDefinitionQuerySchema, entityDefinitionSchema } from '@kbn/entities-schema';
|
||||
import { z } from '@kbn/zod';
|
||||
import { EntityDefinitionIdInvalid } from '../../lib/entities/errors/entity_definition_id_invalid';
|
||||
import { EntityIdConflict } from '../../lib/entities/errors/entity_id_conflict_error';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { EntityDefinitionIdInvalid } from '../../lib/entities/errors/entity_definition_id_invalid';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -50,47 +45,39 @@ import { EntityDefinitionIdInvalid } from '../../lib/entities/errors/entity_defi
|
|||
* 409:
|
||||
* description: An entity definition with this ID already exists
|
||||
* 400:
|
||||
* description: The entity definition cannot be installed; see the error for more details
|
||||
* description: The entity definition cannot be installed; see the error for more details but commonly due to validation failures of the definition ID or metrics format
|
||||
*/
|
||||
export function createEntityDefinitionRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
getScopedClient,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.post<unknown, CreateEntityDefinitionQuery, EntityDefinition>(
|
||||
{
|
||||
path: '/internal/entities/definition',
|
||||
validate: {
|
||||
body: entityDefinitionSchema.strict(),
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
},
|
||||
},
|
||||
async (context, request, res) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
const definition = await client.createEntityDefinition({
|
||||
definition: request.body,
|
||||
installOnly: request.query.installOnly,
|
||||
});
|
||||
export const createEntityDefinitionRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'POST /internal/entities/definition',
|
||||
params: z.object({
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
body: entityDefinitionSchema,
|
||||
}),
|
||||
handler: async ({ request, response, params, logger, getScopedClient }) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
const definition = await client.createEntityDefinition({
|
||||
definition: params.body,
|
||||
installOnly: params.query.installOnly,
|
||||
});
|
||||
|
||||
return res.ok({ body: definition });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return response.ok({ body: definition });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntityDefinitionIdInvalid) {
|
||||
return res.badRequest({ body: e });
|
||||
}
|
||||
|
||||
if (e instanceof EntityIdConflict) {
|
||||
return res.conflict({ body: e });
|
||||
}
|
||||
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return res.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
|
||||
return res.customError({ body: e, statusCode: 500 });
|
||||
if (e instanceof EntityDefinitionIdInvalid) {
|
||||
return response.badRequest({ body: e });
|
||||
}
|
||||
|
||||
if (e instanceof EntityIdConflict) {
|
||||
return response.conflict({ body: e });
|
||||
}
|
||||
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return response.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import {
|
||||
deleteEntityDefinitionParamsSchema,
|
||||
deleteEntityDefinitionQuerySchema,
|
||||
} from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { z } from '@kbn/zod';
|
||||
import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -49,39 +49,31 @@ import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_f
|
|||
* 404:
|
||||
* description: Entity definition with given ID not found
|
||||
*/
|
||||
export function deleteEntityDefinitionRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
getScopedClient,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.delete<{ id: string }, { deleteData?: boolean }, unknown>(
|
||||
{
|
||||
path: '/internal/entities/definition/{id}',
|
||||
validate: {
|
||||
params: deleteEntityDefinitionParamsSchema.strict(),
|
||||
query: deleteEntityDefinitionQuerySchema.strict(),
|
||||
},
|
||||
},
|
||||
async (context, request, res) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
await client.deleteEntityDefinition({
|
||||
id: request.params.id,
|
||||
deleteData: request.query.deleteData,
|
||||
});
|
||||
export const deleteEntityDefinitionRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'DELETE /internal/entities/definition/{id}',
|
||||
params: z.object({
|
||||
path: deleteEntityDefinitionParamsSchema,
|
||||
query: deleteEntityDefinitionQuerySchema,
|
||||
}),
|
||||
handler: async ({ request, response, params, logger, getScopedClient }) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
await client.deleteEntityDefinition({
|
||||
id: params.path.id,
|
||||
deleteData: params.query.deleteData,
|
||||
});
|
||||
|
||||
return res.ok({ body: { acknowledged: true } });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return response.ok({ body: { acknowledged: true } });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntityDefinitionNotFound) {
|
||||
return res.notFound({ body: e });
|
||||
}
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return res.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return res.customError({ body: e, statusCode: 500 });
|
||||
if (e instanceof EntityDefinitionNotFound) {
|
||||
return response.notFound({ body: e });
|
||||
}
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return response.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from '@kbn/zod';
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { getEntityDefinitionQuerySchema } from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { z } from '@kbn/zod';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -49,32 +48,23 @@ import { SetupRouteOptions } from '../types';
|
|||
* running:
|
||||
* type: boolean
|
||||
*/
|
||||
export function getEntityDefinitionRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
getScopedClient,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.get<{ id?: string }, { page?: number; perPage?: number }, unknown>(
|
||||
{
|
||||
path: '/internal/entities/definition/{id?}',
|
||||
validate: {
|
||||
query: getEntityDefinitionQuerySchema.strict(),
|
||||
params: z.object({ id: z.optional(z.string()) }),
|
||||
},
|
||||
},
|
||||
async (context, request, res) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
const result = await client.getEntityDefinitions({
|
||||
page: request.query.page,
|
||||
perPage: request.query.perPage,
|
||||
});
|
||||
export const getEntityDefinitionRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'GET /internal/entities/definition',
|
||||
params: z.object({
|
||||
query: getEntityDefinitionQuerySchema,
|
||||
}),
|
||||
handler: async ({ request, response, params, logger, getScopedClient }) => {
|
||||
try {
|
||||
const client = await getScopedClient({ request });
|
||||
const result = await client.getEntityDefinitions({
|
||||
page: params?.query?.page,
|
||||
perPage: params?.query?.perPage,
|
||||
});
|
||||
|
||||
return res.ok({ body: result });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return res.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
return response.ok({ body: result });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { createEntityDefinitionRoute } from './create';
|
||||
import { deleteEntityDefinitionRoute } from './delete';
|
||||
import { getEntityDefinitionRoute } from './get';
|
||||
import { resetEntityDefinitionRoute } from './reset';
|
||||
import { updateEntityDefinitionRoute } from './update';
|
||||
|
||||
export const entitiesRoutes = {
|
||||
...createEntityDefinitionRoute,
|
||||
...deleteEntityDefinitionRoute,
|
||||
...getEntityDefinitionRoute,
|
||||
...resetEntityDefinitionRoute,
|
||||
...updateEntityDefinitionRoute,
|
||||
};
|
|
@ -5,22 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { resetEntityDefinitionParamsSchema } from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { readEntityDefinition } from '../../lib/entities/read_entity_definition';
|
||||
import {
|
||||
stopAndDeleteHistoryBackfillTransform,
|
||||
stopAndDeleteHistoryTransform,
|
||||
stopAndDeleteLatestTransform,
|
||||
} from '../../lib/entities/stop_and_delete_transform';
|
||||
import {
|
||||
deleteHistoryIngestPipeline,
|
||||
deleteLatestIngestPipeline,
|
||||
} from '../../lib/entities/delete_ingest_pipeline';
|
||||
import { deleteIndices } from '../../lib/entities/delete_index';
|
||||
import { z } from '@kbn/zod';
|
||||
import {
|
||||
createAndInstallHistoryIngestPipeline,
|
||||
createAndInstallLatestIngestPipeline,
|
||||
|
@ -30,60 +16,67 @@ import {
|
|||
createAndInstallHistoryTransform,
|
||||
createAndInstallLatestTransform,
|
||||
} from '../../lib/entities/create_and_install_transform';
|
||||
import { startTransform } from '../../lib/entities/start_transform';
|
||||
import { deleteIndices } from '../../lib/entities/delete_index';
|
||||
import {
|
||||
deleteHistoryIngestPipeline,
|
||||
deleteLatestIngestPipeline,
|
||||
} from '../../lib/entities/delete_ingest_pipeline';
|
||||
import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { isBackfillEnabled } from '../../lib/entities/helpers/is_backfill_enabled';
|
||||
import { readEntityDefinition } from '../../lib/entities/read_entity_definition';
|
||||
import { startTransform } from '../../lib/entities/start_transform';
|
||||
import {
|
||||
stopAndDeleteHistoryBackfillTransform,
|
||||
stopAndDeleteHistoryTransform,
|
||||
stopAndDeleteLatestTransform,
|
||||
} from '../../lib/entities/stop_and_delete_transform';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
export function resetEntityDefinitionRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
logger,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.post<{ id: string }, unknown, unknown>(
|
||||
{
|
||||
path: '/internal/entities/definition/{id}/_reset',
|
||||
validate: {
|
||||
params: resetEntityDefinitionParamsSchema.strict(),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
try {
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
export const resetEntityDefinitionRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'POST /internal/entities/definition/{id}/_reset',
|
||||
params: z.object({
|
||||
path: resetEntityDefinitionParamsSchema,
|
||||
}),
|
||||
handler: async ({ context, response, params, logger }) => {
|
||||
try {
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
||||
const definition = await readEntityDefinition(soClient, req.params.id, logger);
|
||||
const definition = await readEntityDefinition(soClient, params.path.id, logger);
|
||||
|
||||
// Delete the transform and ingest pipeline
|
||||
await stopAndDeleteHistoryTransform(esClient, definition, logger);
|
||||
if (isBackfillEnabled(definition)) {
|
||||
await stopAndDeleteHistoryBackfillTransform(esClient, definition, logger);
|
||||
}
|
||||
await stopAndDeleteLatestTransform(esClient, definition, logger);
|
||||
await deleteHistoryIngestPipeline(esClient, definition, logger);
|
||||
await deleteLatestIngestPipeline(esClient, definition, logger);
|
||||
await deleteIndices(esClient, definition, logger);
|
||||
|
||||
// Recreate everything
|
||||
await createAndInstallHistoryIngestPipeline(esClient, definition, logger);
|
||||
await createAndInstallLatestIngestPipeline(esClient, definition, logger);
|
||||
await createAndInstallHistoryTransform(esClient, definition, logger);
|
||||
if (isBackfillEnabled(definition)) {
|
||||
await createAndInstallHistoryBackfillTransform(esClient, definition, logger);
|
||||
}
|
||||
await createAndInstallLatestTransform(esClient, definition, logger);
|
||||
await startTransform(esClient, definition, logger);
|
||||
|
||||
return res.ok({ body: { acknowledged: true } });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntityDefinitionNotFound) {
|
||||
return res.notFound({ body: e });
|
||||
}
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return res.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return res.customError({ body: e, statusCode: 500 });
|
||||
// Delete the transform and ingest pipeline
|
||||
await stopAndDeleteHistoryTransform(esClient, definition, logger);
|
||||
if (isBackfillEnabled(definition)) {
|
||||
await stopAndDeleteHistoryBackfillTransform(esClient, definition, logger);
|
||||
}
|
||||
await stopAndDeleteLatestTransform(esClient, definition, logger);
|
||||
await deleteHistoryIngestPipeline(esClient, definition, logger);
|
||||
await deleteLatestIngestPipeline(esClient, definition, logger);
|
||||
await deleteIndices(esClient, definition, logger);
|
||||
|
||||
// Recreate everything
|
||||
await createAndInstallHistoryIngestPipeline(esClient, definition, logger);
|
||||
await createAndInstallLatestIngestPipeline(esClient, definition, logger);
|
||||
await createAndInstallHistoryTransform(esClient, definition, logger);
|
||||
if (isBackfillEnabled(definition)) {
|
||||
await createAndInstallHistoryBackfillTransform(esClient, definition, logger);
|
||||
}
|
||||
await createAndInstallLatestTransform(esClient, definition, logger);
|
||||
await startTransform(esClient, definition, logger);
|
||||
|
||||
return response.ok({ body: { acknowledged: true } });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntityDefinitionNotFound) {
|
||||
return response.notFound({ body: e });
|
||||
}
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return response.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,28 +5,25 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from '@kbn/zod';
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import {
|
||||
createEntityDefinitionQuerySchema,
|
||||
CreateEntityDefinitionQuery,
|
||||
entityDefinitionUpdateSchema,
|
||||
EntityDefinitionUpdate,
|
||||
} from '@kbn/entities-schema';
|
||||
import { SetupRouteOptions } from '../types';
|
||||
import { z } from '@kbn/zod';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { startTransform } from '../../lib/entities/start_transform';
|
||||
import { findEntityDefinitionById } from '../../lib/entities/find_entity_definition';
|
||||
import {
|
||||
installationInProgress,
|
||||
reinstallEntityDefinition,
|
||||
} from '../../lib/entities/install_entity_definition';
|
||||
import { findEntityDefinitionById } from '../../lib/entities/find_entity_definition';
|
||||
import { startTransform } from '../../lib/entities/start_transform';
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /internal/entities/definition:
|
||||
* put:
|
||||
* patch:
|
||||
* description: Update an entity definition.
|
||||
* tags:
|
||||
* - definitions
|
||||
|
@ -61,71 +58,63 @@ import { findEntityDefinitionById } from '../../lib/entities/find_entity_definit
|
|||
* 409:
|
||||
* description: The entity definition is being updated by another request
|
||||
*/
|
||||
export function updateEntityDefinitionRoute<T extends RequestHandlerContext>({
|
||||
router,
|
||||
server,
|
||||
}: SetupRouteOptions<T>) {
|
||||
router.patch<{ id: string }, CreateEntityDefinitionQuery, EntityDefinitionUpdate>(
|
||||
{
|
||||
path: '/internal/entities/definition/{id}',
|
||||
validate: {
|
||||
body: entityDefinitionUpdateSchema.strict(),
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
params: z.object({ id: z.string() }),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
const { logger } = server;
|
||||
const core = await context.core;
|
||||
const soClient = core.savedObjects.client;
|
||||
const esClient = core.elasticsearch.client.asCurrentUser;
|
||||
export const updateEntityDefinitionRoute = createEntityManagerServerRoute({
|
||||
endpoint: 'PATCH /internal/entities/definition/{id}',
|
||||
params: z.object({
|
||||
path: z.object({ id: z.string() }),
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
body: entityDefinitionUpdateSchema,
|
||||
}),
|
||||
handler: async ({ context, response, params, logger }) => {
|
||||
const core = await context.core;
|
||||
const soClient = core.savedObjects.client;
|
||||
const esClient = core.elasticsearch.client.asCurrentUser;
|
||||
|
||||
try {
|
||||
const installedDefinition = await findEntityDefinitionById({
|
||||
soClient,
|
||||
esClient,
|
||||
id: req.params.id,
|
||||
try {
|
||||
const installedDefinition = await findEntityDefinitionById({
|
||||
soClient,
|
||||
esClient,
|
||||
id: params.path.id,
|
||||
});
|
||||
|
||||
if (!installedDefinition) {
|
||||
return response.notFound({
|
||||
body: { message: `Entity definition [${params.path.id}] not found` },
|
||||
});
|
||||
|
||||
if (!installedDefinition) {
|
||||
return res.notFound({
|
||||
body: { message: `Entity definition [${req.params.id}] not found` },
|
||||
});
|
||||
}
|
||||
|
||||
if (installedDefinition.managed) {
|
||||
return res.forbidden({
|
||||
body: { message: `Managed definition cannot be modified` },
|
||||
});
|
||||
}
|
||||
|
||||
if (installationInProgress(installedDefinition)) {
|
||||
return res.conflict({
|
||||
body: { message: `Entity definition [${req.params.id}] has changes in progress` },
|
||||
});
|
||||
}
|
||||
|
||||
const updatedDefinition = await reinstallEntityDefinition({
|
||||
soClient,
|
||||
esClient,
|
||||
logger,
|
||||
definition: installedDefinition,
|
||||
definitionUpdate: req.body,
|
||||
});
|
||||
|
||||
if (!req.query.installOnly) {
|
||||
await startTransform(esClient, updatedDefinition, logger);
|
||||
}
|
||||
|
||||
return res.ok({ body: updatedDefinition });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return res.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return res.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
|
||||
if (installedDefinition.managed) {
|
||||
return response.forbidden({
|
||||
body: { message: `Managed definition cannot be modified` },
|
||||
});
|
||||
}
|
||||
|
||||
if (installationInProgress(installedDefinition)) {
|
||||
return response.conflict({
|
||||
body: { message: `Entity definition [${params.path.id}] has changes in progress` },
|
||||
});
|
||||
}
|
||||
|
||||
const updatedDefinition = await reinstallEntityDefinition({
|
||||
soClient,
|
||||
esClient,
|
||||
logger,
|
||||
definition: installedDefinition,
|
||||
definitionUpdate: params.body,
|
||||
});
|
||||
|
||||
if (!params.query.installOnly) {
|
||||
await startTransform(esClient, updatedDefinition, logger);
|
||||
}
|
||||
|
||||
return response.ok({ body: updatedDefinition });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return response.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,24 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from '@kbn/core/server';
|
||||
import { SetupRouteOptions } from './types';
|
||||
import { createEntityDefinitionRoute } from './entities/create';
|
||||
import { deleteEntityDefinitionRoute } from './entities/delete';
|
||||
import { resetEntityDefinitionRoute } from './entities/reset';
|
||||
import { getEntityDefinitionRoute } from './entities/get';
|
||||
import { updateEntityDefinitionRoute } from './entities/update';
|
||||
import { checkEntityDiscoveryEnabledRoute } from './enablement/check';
|
||||
import { enableEntityDiscoveryRoute } from './enablement/enable';
|
||||
import { disableEntityDiscoveryRoute } from './enablement/disable';
|
||||
import { enablementRoutes } from './enablement';
|
||||
import { entitiesRoutes } from './entities';
|
||||
|
||||
export function setupRoutes<T extends RequestHandlerContext>(dependencies: SetupRouteOptions<T>) {
|
||||
createEntityDefinitionRoute<T>(dependencies);
|
||||
deleteEntityDefinitionRoute<T>(dependencies);
|
||||
resetEntityDefinitionRoute<T>(dependencies);
|
||||
getEntityDefinitionRoute<T>(dependencies);
|
||||
checkEntityDiscoveryEnabledRoute<T>(dependencies);
|
||||
enableEntityDiscoveryRoute<T>(dependencies);
|
||||
disableEntityDiscoveryRoute<T>(dependencies);
|
||||
updateEntityDefinitionRoute<T>(dependencies);
|
||||
}
|
||||
export const entityManagerRouteRepository = {
|
||||
...enablementRoutes,
|
||||
...entitiesRoutes,
|
||||
};
|
||||
|
||||
export type EntityManagerRouteRepository = typeof entityManagerRouteRepository;
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IRouter, KibanaRequest, RequestHandlerContextBase } from '@kbn/core-http-server';
|
||||
import { Logger } from '@kbn/core/server';
|
||||
import { EntityManagerServerSetup } from '../types';
|
||||
import { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { DefaultRouteHandlerResources } from '@kbn/server-route-repository';
|
||||
import { EntityClient } from '../lib/entity_client';
|
||||
import { EntityManagerServerSetup } from '../types';
|
||||
|
||||
export interface SetupRouteOptions<T extends RequestHandlerContextBase> {
|
||||
router: IRouter<T>;
|
||||
export interface EntityManagerRouteDependencies {
|
||||
server: EntityManagerServerSetup;
|
||||
logger: Logger;
|
||||
getScopedClient: ({ request }: { request: KibanaRequest }) => Promise<EntityClient>;
|
||||
}
|
||||
|
||||
export type EntityManagerRouteHandlerResources = EntityManagerRouteDependencies &
|
||||
DefaultRouteHandlerResources;
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
"@kbn/encrypted-saved-objects-plugin",
|
||||
"@kbn/logging-mocks",
|
||||
"@kbn/licensing-plugin",
|
||||
"@kbn/server-route-repository-client",
|
||||
"@kbn/server-route-repository",
|
||||
"@kbn/zod",
|
||||
"@kbn/zod-helpers",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue