diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts index 0b719d8a67df74..393f0ab2dae448 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts @@ -12,6 +12,7 @@ import { PostAgentAcksRequestSchema } from '../../types/rest_spec'; import { AcksService } from '../../services/agents'; import { AgentEvent } from '../../../common/types/models'; import { PostAgentAcksResponse } from '../../../common/types/rest_spec'; +import { defaultIngestErrorHandler } from '../../errors'; export const postAgentAcksHandlerBuilder = function ( ackService: AcksService @@ -50,18 +51,8 @@ export const postAgentAcksHandlerBuilder = function ( }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index 81893b1e78338c..1e06a645310272 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -12,6 +12,7 @@ import { PostNewAgentActionRequestSchema } from '../../types/rest_spec'; import { ActionsService } from '../../services/agents'; import { NewAgentAction } from '../../../common/types/models'; import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; +import { defaultIngestErrorHandler } from '../../errors'; export const postNewAgentActionHandlerBuilder = function ( actionsService: ActionsService @@ -40,18 +41,8 @@ export const postNewAgentActionHandlerBuilder = function ( }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index e485fad09ba995..4da00d73a24099 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -30,6 +30,7 @@ import { import * as AgentService from '../../services/agents'; import * as APIKeyService from '../../services/api_keys'; import { appContextService } from '../../services/app_context'; +import { defaultIngestErrorHandler } from '../../errors'; export const getAgentHandler: RequestHandler= 500) { - logger.error(err); - } - - return response.customError({ - statusCode: err.output.statusCode, - body: { message: err.output.payload.message }, - }); - } - - logger.error(err); - - return response.customError({ - statusCode: 500, - body: { message: err.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -256,18 +231,8 @@ export const postAgentEnrollHandler: RequestHandler< }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -316,11 +281,8 @@ export const putAgentsReassignHandler: RequestHandler< success: true, }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -336,10 +298,7 @@ export const getAgentStatusForConfigHandler: RequestHandler< const body: GetAgentStatusResponse = { results, success: true }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts index d1e54fe4fb3a17..8e5f7951ed7e8e 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts @@ -9,6 +9,7 @@ import { TypeOf } from '@kbn/config-schema'; import { PostAgentUnenrollResponse } from '../../../common/types'; import { PostAgentUnenrollRequestSchema } from '../../types'; import * as AgentService from '../../services/agents'; +import { defaultIngestErrorHandler } from '../../errors'; export const postAgentsUnenrollHandler: RequestHandler< TypeOf, @@ -27,10 +28,7 @@ export const postAgentsUnenrollHandler: RequestHandler< success: true, }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts new file mode 100644 index 00000000000000..311b3bbf7f13ba --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts @@ -0,0 +1,278 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { TypeOf } from '@kbn/config-schema'; +import { RequestHandler, ResponseHeaders } from 'src/core/server'; +import bluebird from 'bluebird'; +import { fullAgentPolicyToYaml } from '../../../common/services'; +import { appContextService, agentPolicyService, packagePolicyService } from '../../services'; +import { listAgents } from '../../services/agents'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { + GetAgentPoliciesRequestSchema, + GetOneAgentPolicyRequestSchema, + CreateAgentPolicyRequestSchema, + UpdateAgentPolicyRequestSchema, + CopyAgentPolicyRequestSchema, + DeleteAgentPolicyRequestSchema, + GetFullAgentPolicyRequestSchema, + AgentPolicy, + DefaultPackages, + NewPackagePolicy, +} from '../../types'; +import { + GetAgentPoliciesResponse, + GetAgentPoliciesResponseItem, + GetOneAgentPolicyResponse, + CreateAgentPolicyResponse, + UpdateAgentPolicyResponse, + CopyAgentPolicyResponse, + DeleteAgentPolicyResponse, + GetFullAgentPolicyResponse, +} from '../../../common'; +import { defaultIngestErrorHandler } from '../../errors'; + +export const getAgentPoliciesHandler: RequestHandler< + undefined, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const { full: withPackagePolicies = false, ...restOfQuery } = request.query; + try { + const { items, total, page, perPage } = await agentPolicyService.list(soClient, { + withPackagePolicies, + ...restOfQuery, + }); + const body: GetAgentPoliciesResponse = { + items, + total, + page, + perPage, + }; + + await bluebird.map( + items, + (agentPolicy: GetAgentPoliciesResponseItem) => + listAgents(soClient, { + showInactive: false, + perPage: 0, + page: 1, + kuery: `${AGENT_SAVED_OBJECT_TYPE}.policy_id:${agentPolicy.id}`, + }).then(({ total: agentTotal }) => (agentPolicy.agents = agentTotal)), + { concurrency: 10 } + ); + + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const getOneAgentPolicyHandler: RequestHandler> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + try { + const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); + if (agentPolicy) { + const body: GetOneAgentPolicyResponse = { + item: agentPolicy, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); + } + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const createAgentPolicyHandler: RequestHandler< + undefined, + TypeOf, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + const withSysMonitoring = request.query.sys_monitoring ?? false; + try { + // eslint-disable-next-line prefer-const + let [agentPolicy, newSysPackagePolicy] = await Promise.all< + AgentPolicy, + NewPackagePolicy | undefined + >([ + agentPolicyService.create(soClient, request.body, { + user, + }), + // If needed, retrieve System package information and build a new package policy for the system package + // NOTE: we ignore failures in attempting to create package policy, since agent policy might have been created + // successfully + withSysMonitoring + ? packagePolicyService + .buildPackagePolicyFromPackage(soClient, DefaultPackages.system) + .catch(() => undefined) + : undefined, + ]); + + // Create the system monitoring package policy and add it to agent policy. + if (withSysMonitoring && newSysPackagePolicy !== undefined && agentPolicy !== undefined) { + newSysPackagePolicy.policy_id = agentPolicy.id; + newSysPackagePolicy.namespace = agentPolicy.namespace; + await packagePolicyService.create(soClient, callCluster, newSysPackagePolicy, { + user, + bumpRevision: false, + }); + } + + const body: CreateAgentPolicyResponse = { + item: agentPolicy, + }; + + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const updateAgentPolicyHandler: RequestHandler< + TypeOf, + unknown, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); + try { + const agentPolicy = await agentPolicyService.update( + soClient, + request.params.agentPolicyId, + request.body, + { + user: user || undefined, + } + ); + const body: UpdateAgentPolicyResponse = { item: agentPolicy }; + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const copyAgentPolicyHandler: RequestHandler< + TypeOf, + unknown, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); + try { + const agentPolicy = await agentPolicyService.copy( + soClient, + request.params.agentPolicyId, + request.body, + { + user: user || undefined, + } + ); + const body: CopyAgentPolicyResponse = { item: agentPolicy }; + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const deleteAgentPoliciesHandler: RequestHandler< + unknown, + unknown, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + try { + const body: DeleteAgentPolicyResponse = await agentPolicyService.delete( + soClient, + request.body.agentPolicyId + ); + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const getFullAgentPolicy: RequestHandler< + TypeOf, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + + try { + const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy( + soClient, + request.params.agentPolicyId, + { standalone: request.query.standalone === true } + ); + if (fullAgentPolicy) { + const body: GetFullAgentPolicyResponse = { + item: fullAgentPolicy, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); + } + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const downloadFullAgentPolicy: RequestHandler< + TypeOf, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const { + params: { agentPolicyId }, + } = request; + + try { + const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, agentPolicyId, { + standalone: request.query.standalone === true, + }); + if (fullAgentPolicy) { + const body = fullAgentPolicyToYaml(fullAgentPolicy); + const headers: ResponseHeaders = { + 'content-type': 'text/x-yaml', + 'content-disposition': `attachment; filename="elastic-agent.yml"`, + }; + return response.ok({ + body, + headers, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); + } + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts index 43ae2b72f6077c..652a7789f65a30 100644 --- a/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts @@ -7,6 +7,7 @@ import { RequestHandler, SavedObjectsClientContract } from 'src/core/server'; import { DataStream } from '../../types'; import { GetDataStreamsResponse, KibanaAssetType } from '../../../common'; import { getPackageSavedObjects, getKibanaSavedObject } from '../../services/epm/packages/get'; +import { defaultIngestErrorHandler } from '../../errors'; const DATA_STREAM_INDEX_PATTERN = 'logs-*-*,metrics-*-*'; @@ -157,11 +158,8 @@ export const getListHandler: RequestHandler = async (context, request, response) return response.ok({ body, }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts index 94fc6de6096139..c0dff841d86f40 100644 --- a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts @@ -19,6 +19,7 @@ import { PostEnrollmentAPIKeyResponse, } from '../../../common'; import * as APIKeyService from '../../services/api_keys'; +import { defaultIngestErrorHandler } from '../../errors'; export const getEnrollmentApiKeysHandler: RequestHandler< undefined, @@ -34,11 +35,8 @@ export const getEnrollmentApiKeysHandler: RequestHandler< const body: GetEnrollmentAPIKeysResponse = { list: items, success: true, total, page, perPage }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; export const postEnrollmentApiKeyHandler: RequestHandler< @@ -57,17 +55,8 @@ export const postEnrollmentApiKeyHandler: RequestHandler< const body: PostEnrollmentAPIKeyResponse = { item: apiKey, success: true, action: 'created' }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -81,16 +70,13 @@ export const deleteEnrollmentApiKeyHandler: RequestHandler { const soClient = context.core.savedObjects.client; @@ -24,11 +25,8 @@ export const getOutputsHandler: RequestHandler = async (context, request, respon }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -45,17 +43,14 @@ export const getOneOuputHandler: RequestHandler +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + try { + const { items, total, page, perPage } = await packagePolicyService.list( + soClient, + request.query + ); + return response.ok({ + body: { + items, + total, + page, + perPage, + }, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const getOnePackagePolicyHandler: RequestHandler> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const { packagePolicyId } = request.params; + const notFoundResponse = () => + response.notFound({ body: { message: `Package policy ${packagePolicyId} not found` } }); + + try { + const packagePolicy = await packagePolicyService.get(soClient, packagePolicyId); + if (packagePolicy) { + return response.ok({ + body: { + item: packagePolicy, + }, + }); + } else { + return notFoundResponse(); + } + } catch (error) { + if (SavedObjectsErrorHelpers.isNotFoundError(error)) { + return notFoundResponse(); + } else { + return defaultIngestErrorHandler({ error, response }); + } + } +}; + +export const createPackagePolicyHandler: RequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + const logger = appContextService.getLogger(); + let newData = { ...request.body }; + try { + // If we have external callbacks, then process those now before creating the actual package policy + const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyCreate'); + if (externalCallbacks && externalCallbacks.size > 0) { + let updatedNewData: NewPackagePolicy = newData; + + for (const callback of externalCallbacks) { + try { + // ensure that the returned value by the callback passes schema validation + updatedNewData = CreatePackagePolicyRequestSchema.body.validate( + await callback(updatedNewData) + ); + } catch (error) { + // Log the error, but keep going and process the other callbacks + logger.error( + 'An external registered [packagePolicyCreate] callback failed when executed' + ); + logger.error(error); + } + } + + newData = updatedNewData; + } + + // Create package policy + const packagePolicy = await packagePolicyService.create(soClient, callCluster, newData, { + user, + }); + const body: CreatePackagePolicyResponse = { item: packagePolicy }; + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const updatePackagePolicyHandler: RequestHandler< + TypeOf, + unknown, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + try { + const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId); + + if (!packagePolicy) { + throw Boom.notFound('Package policy not found'); + } + + const newData = { ...request.body }; + const pkg = newData.package || packagePolicy.package; + const inputs = newData.inputs || packagePolicy.inputs; + if (pkg && (newData.inputs || newData.package)) { + const pkgInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: pkg.name, + pkgVersion: pkg.version, + }); + newData.inputs = (await packagePolicyService.assignPackageStream(pkgInfo, inputs)) as TypeOf< + typeof CreatePackagePolicyRequestSchema.body + >['inputs']; + } + + const updatedPackagePolicy = await packagePolicyService.update( + soClient, + request.params.packagePolicyId, + newData, + { user } + ); + return response.ok({ + body: { item: updatedPackagePolicy }, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + +export const deletePackagePolicyHandler: RequestHandler< + unknown, + unknown, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + try { + const body: DeletePackagePoliciesResponse = await packagePolicyService.delete( + soClient, + request.body.packagePolicyIds, + { user } + ); + return response.ok({ + body, + }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/settings/index.ts b/x-pack/plugins/ingest_manager/server/routes/settings/index.ts index 56e666056e8d0a..315fb0ee0cedc1 100644 --- a/x-pack/plugins/ingest_manager/server/routes/settings/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/settings/index.ts @@ -7,8 +7,8 @@ import { IRouter, RequestHandler } from 'src/core/server'; import { TypeOf } from '@kbn/config-schema'; import { PLUGIN_ID, SETTINGS_API_ROUTES } from '../../constants'; import { PutSettingsRequestSchema, GetSettingsRequestSchema } from '../../types'; - import { settingsService } from '../../services'; +import { defaultIngestErrorHandler } from '../../errors'; export const getSettingsHandler: RequestHandler = async (context, request, response) => { const soClient = context.core.savedObjects.client; @@ -20,17 +20,14 @@ export const getSettingsHandler: RequestHandler = async (context, request, respo item: settings, }; return response.ok({ body }); - } catch (e) { - if (e.isBoom && e.output.statusCode === 404) { + } catch (error) { + if (error.isBoom && error.output.statusCode === 404) { return response.notFound({ body: { message: `Setings not found` }, }); } - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + return defaultIngestErrorHandler({ error, response }); } }; @@ -47,17 +44,14 @@ export const putSettingsHandler: RequestHandler< item: settings, }; return response.ok({ body }); - } catch (e) { - if (e.isBoom && e.output.statusCode === 404) { + } catch (error) { + if (error.isBoom && error.output.statusCode === 404) { return response.notFound({ body: { message: `Setings not found` }, }); } - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts new file mode 100644 index 00000000000000..71e2bbc4e4c0a0 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('ingest_manager_agent_policies', () => { + describe('POST /api/ingest_manager/agent_policies', () => { + it('should work with valid values', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'TEST', + namespace: 'default', + }) + .expect(200); + }); + + it('should return a 400 with an empty namespace', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'TEST', + namespace: '', + }) + .expect(400); + }); + + it('should return a 400 with an invalid namespace', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'TEST', + namespace: 'InvalidNamespace', + }) + .expect(400); + }); + }); + + describe('POST /api/ingest_manager/agent_policies/{agentPolicyId}/copy', () => { + before(async () => { + await esArchiver.loadIfNeeded('fleet/agents'); + }); + after(async () => { + await esArchiver.unload('fleet/agents'); + }); + + const TEST_POLICY_ID = 'policy1'; + + it('should work with valid values', async () => { + const { + body: { item }, + } = await supertest + .post(`/api/ingest_manager/agent_policies/${TEST_POLICY_ID}/copy`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Copied policy', + description: 'Test', + }) + .expect(200); + // eslint-disable-next-line @typescript-eslint/naming-convention + const { id, updated_at, ...newPolicy } = item; + + expect(newPolicy).to.eql({ + name: 'Copied policy', + description: 'Test', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + revision: 1, + updated_by: 'elastic', + package_policies: [], + }); + }); + + it('should return a 404 with invalid source policy', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies/INVALID_POLICY_ID/copy`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Copied policy', + description: '', + }) + .expect(404); + }); + + it('should return a 400 with invalid payload', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies/${TEST_POLICY_ID}/copy`) + .set('kbn-xsrf', 'xxxx') + .send({}) + .expect(400); + }); + + it('should return a 400 with invalid name', async () => { + await supertest + .post(`/api/ingest_manager/agent_policies/${TEST_POLICY_ID}/copy`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: '', + }) + .expect(400); + }); + }); + }); +}