From d1e13ef89563da8ed4a896c0ba032509b2007e4e Mon Sep 17 00:00:00 2001 From: Luke G <11671118+lgestc@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:04:35 +0200 Subject: [PATCH] [Security Solution] Add versioned router for timelines #7144 (#166729) ## Summary This PR introduces versioned router for timeline apis, as per https://github.com/elastic/security-team/issues/7144 --- .../public/timelines/containers/api.test.ts | 6 + .../public/timelines/containers/api.ts | 17 ++- .../public/timelines/containers/notes/api.ts | 1 + .../timelines/containers/pinned_event/api.ts | 1 + .../clean_draft_timelines/index.ts | 127 +++++++++-------- .../get_draft_timelines/index.ts | 93 ++++++------ .../lib/timeline/routes/notes/delete_note.ts | 63 ++++---- .../lib/timeline/routes/notes/persist_note.ts | 69 ++++----- .../pinned_events/persist_pinned_event.ts | 65 +++++---- .../install_prepackaged_timelines/index.ts | 99 +++++++------ .../timelines/create_timelines/index.ts | 115 ++++++++------- .../timelines/delete_timelines/index.ts | 47 +++--- .../timelines/export_timelines/index.ts | 83 ++++++----- .../routes/timelines/get_timeline/index.ts | 63 ++++---- .../routes/timelines/get_timelines/index.ts | 134 +++++++++--------- .../timelines/import_timelines/index.ts | 81 ++++++----- .../routes/timelines/patch_timelines/index.ts | 115 ++++++++------- .../timelines/persist_favorite/index.ts | 71 +++++----- .../timelines/resolve_timeline/index.ts | 67 +++++---- 19 files changed, 710 insertions(+), 607 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts index cfac0b1d4b243..93ef3f5b9cab4 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts @@ -144,6 +144,7 @@ describe('persistTimeline', () => { body: JSON.stringify({ timelineType: initialDraftTimeline.timelineType, }), + version: '2023-10-31', }); }); @@ -346,6 +347,7 @@ describe('importTimelines', () => { headers: { 'Content-Type': undefined }, body: new FormData(), signal: undefined, + version: '2023-10-31', }) ); }); @@ -377,6 +379,7 @@ describe('exportSelectedTimeline', () => { method: 'POST', query: { file_name: 'timelines_export.ndjson' }, signal: {}, + version: '2023-10-31', }); }); }); @@ -400,6 +403,7 @@ describe('getDraftTimeline', () => { test('should pass correct args to KibanaServices', () => { expect(getMock).toBeCalledWith('/api/timeline/_draft', { query: timelineType, + version: '2023-10-31', }); }); }); @@ -425,6 +429,7 @@ describe('cleanDraftTimeline', () => { expect(postMock).toBeCalledWith('/api/timeline/_draft', { body: JSON.stringify(args), + version: '2023-10-31', }); }); @@ -439,6 +444,7 @@ describe('cleanDraftTimeline', () => { expect(postMock).toBeCalledWith('/api/timeline/_draft', { body: JSON.stringify(args), + version: '2023-10-31', }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.ts index 8d1064c5e9c2d..72a75027e417e 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.ts @@ -11,6 +11,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { isEmpty } from 'lodash'; import { throwErrors } from '@kbn/cases-plugin/common'; + import type { TimelineResponse, TimelineErrorResponse, @@ -119,6 +120,7 @@ const postTimeline = async ({ const response = await KibanaServices.get().http.post(TIMELINE_URL, { method: 'POST', body: requestBody, + version: '2023-10-31', }); return decodeTimelineResponse(response); @@ -140,6 +142,7 @@ const patchTimeline = async ({ response = await KibanaServices.get().http.patch(TIMELINE_URL, { method: 'PATCH', body: requestBody, + version: '2023-10-31', }); } catch (err) { // For Future developer @@ -228,6 +231,7 @@ export const importTimelines = async ({ headers: { 'Content-Type': undefined }, body: formData, signal, + version: '2023-10-31', }); }; @@ -249,6 +253,7 @@ export const exportSelectedTimeline = ({ file_name: filename, }, signal, + version: '2023-10-31', }); }; @@ -261,6 +266,7 @@ export const getDraftTimeline = async ({ query: { timelineType, }, + version: '2023-10-31', }); return decodeTimelineResponse(response); @@ -293,6 +299,7 @@ export const cleanDraftTimeline = async ({ } const response = await KibanaServices.get().http.post(TIMELINE_DRAFT_URL, { body: requestBody, + version: '2023-10-31', }); return decodeTimelineResponse(response); @@ -301,7 +308,9 @@ export const cleanDraftTimeline = async ({ export const installPrepackedTimelines = async (): Promise => { const response = await KibanaServices.get().http.post( TIMELINE_PREPACKAGED_URL, - {} + { + version: '2023-10-31', + } ); return decodePrepackedTimelineResponse(response); @@ -312,6 +321,7 @@ export const getTimeline = async (id: string) => { query: { id, }, + version: '2023-10-31', }); return decodeSingleTimelineResponse(response); @@ -324,6 +334,7 @@ export const resolveTimeline = async (id: string) => { query: { id, }, + version: '2023-10-31', } ); @@ -335,6 +346,7 @@ export const getTimelineTemplate = async (templateTimelineId: string) => { query: { template_timeline_id: templateTimelineId, }, + version: '2023-10-31', }); return decodeSingleTimelineResponse(response); @@ -354,6 +366,7 @@ export const getAllTimelines = async (args: GetTimelinesArgs, abortSignal: Abort ...(args.timelineType ? { timeline_type: args.timelineType } : {}), }, signal: abortSignal, + version: '2023-10-31', }); return decodeAllTimelinesResponse(response); @@ -388,6 +401,7 @@ export const persistFavorite = async ({ { method: 'PATCH', body: requestBody, + version: '2023-10-31', } ); @@ -407,6 +421,7 @@ export const deleteTimelinesByIds = async (savedObjectIds: string[]) => { const response = await KibanaServices.get().http.delete(TIMELINE_URL, { method: 'DELETE', body: requestBody, + version: '2023-10-31', }); return response; }; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/notes/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/notes/api.ts index c9c5bd836f661..25cb18f574260 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/notes/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/notes/api.ts @@ -30,6 +30,7 @@ export const persistNote = async ({ const response = await KibanaServices.get().http.patch(NOTE_URL, { method: 'PATCH', body: requestBody, + version: '2023-10-31', }); return response; }; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/pinned_event/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/pinned_event/api.ts index a7cc370daa463..4e5a7bc9a05a6 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/pinned_event/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/pinned_event/api.ts @@ -26,6 +26,7 @@ export const persistPinnedEvent = async ({ const response = await KibanaServices.get().http.patch(PINNED_EVENT_URL, { method: 'PATCH', body: requestBody, + version: '2023-10-31', }); return response; }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts index 55934342a8cfc..dff25caef5f26 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts @@ -29,81 +29,86 @@ export const cleanDraftTimelinesRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.post( - { + router.versioned + .post({ path: TIMELINE_DRAFT_URL, - validate: { - body: buildRouteValidationWithExcess(cleanDraftTimelineSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(cleanDraftTimelineSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const siemResponse = buildSiemResponse(response); - try { - const { - timeline: [draftTimeline], - } = await getDraftTimeline(frameworkRequest, request.body.timelineType); + try { + const { + timeline: [draftTimeline], + } = await getDraftTimeline(frameworkRequest, request.body.timelineType); - if (draftTimeline?.savedObjectId) { - await resetTimeline( - frameworkRequest, - [draftTimeline.savedObjectId], - request.body.timelineType - ); - const cleanedDraftTimeline = await getTimeline( - frameworkRequest, - draftTimeline.savedObjectId - ); + if (draftTimeline?.savedObjectId) { + await resetTimeline( + frameworkRequest, + [draftTimeline.savedObjectId], + request.body.timelineType + ); + const cleanedDraftTimeline = await getTimeline( + frameworkRequest, + draftTimeline.savedObjectId + ); - return response.ok({ - body: { - data: { - persistTimeline: { - timeline: cleanedDraftTimeline, + return response.ok({ + body: { + data: { + persistTimeline: { + timeline: cleanedDraftTimeline, + }, }, }, - }, - }); - } - const templateTimelineData = - request.body.timelineType === TimelineType.template - ? { - timelineType: request.body.timelineType, - templateTimelineId: uuidv4(), - templateTimelineVersion: 1, - } - : {}; + }); + } + const templateTimelineData = + request.body.timelineType === TimelineType.template + ? { + timelineType: request.body.timelineType, + templateTimelineId: uuidv4(), + templateTimelineVersion: 1, + } + : {}; - const newTimelineResponse = await persistTimeline(frameworkRequest, null, null, { - ...draftTimelineDefaults, - ...templateTimelineData, - }); + const newTimelineResponse = await persistTimeline(frameworkRequest, null, null, { + ...draftTimelineDefaults, + ...templateTimelineData, + }); - if (newTimelineResponse.code === 200) { - return response.ok({ - body: { - data: { - persistTimeline: { - timeline: newTimelineResponse.timeline, + if (newTimelineResponse.code === 200) { + return response.ok({ + body: { + data: { + persistTimeline: { + timeline: newTimelineResponse.timeline, + }, }, }, - }, - }); - } + }); + } - return response.ok({}); - } catch (err) { - const error = transformError(err); + return response.ok({}); + } catch (err) { + const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts index f58b2dc6d6d5f..93e5d9fe84b00 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts @@ -23,63 +23,68 @@ export const getDraftTimelinesRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.get( - { + router.versioned + .get({ path: TIMELINE_DRAFT_URL, - validate: { - query: buildRouteValidationWithExcess(getDraftTimelineSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + validate: { + request: { query: buildRouteValidationWithExcess(getDraftTimelineSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const siemResponse = buildSiemResponse(response); - try { - const { - timeline: [draftTimeline], - } = await getDraftTimeline(frameworkRequest, request.query.timelineType); + try { + const { + timeline: [draftTimeline], + } = await getDraftTimeline(frameworkRequest, request.query.timelineType); - if (draftTimeline?.savedObjectId) { - return response.ok({ - body: { - data: { - persistTimeline: { - timeline: draftTimeline, + if (draftTimeline?.savedObjectId) { + return response.ok({ + body: { + data: { + persistTimeline: { + timeline: draftTimeline, + }, }, }, - }, - }); - } + }); + } - const newTimelineResponse = await persistTimeline(frameworkRequest, null, null, { - ...draftTimelineDefaults, - timelineType: request.query.timelineType, - }); + const newTimelineResponse = await persistTimeline(frameworkRequest, null, null, { + ...draftTimelineDefaults, + timelineType: request.query.timelineType, + }); - if (newTimelineResponse.code === 200) { - return response.ok({ - body: { - data: { - persistTimeline: { - timeline: newTimelineResponse.timeline, + if (newTimelineResponse.code === 200) { + return response.ok({ + body: { + data: { + persistTimeline: { + timeline: newTimelineResponse.timeline, + }, }, }, - }, - }); - } + }); + } - return response.ok({}); - } catch (err) { - const error = transformError(err); + return response.ok({}); + } catch (err) { + const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts index 6ae4872cc236a..d54df1c7830d0 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts @@ -25,38 +25,43 @@ export const deleteNoteRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.delete( - { + router.versioned + .delete({ path: NOTE_URL, - validate: { - body: buildRouteValidationWithExcess(deleteNoteSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const siemResponse = buildSiemResponse(response); - - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const noteId = request.body?.noteId ?? ''; - - const res = await deleteNote({ - request: frameworkRequest, - noteId, - }); - - return response.ok({ - body: { data: { persistNote: res } }, - }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(deleteNoteSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const noteId = request.body?.noteId ?? ''; + + const res = await deleteNote({ + request: frameworkRequest, + noteId, + }); + + return response.ok({ + body: { data: { persistNote: res } }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts index 36da9a034cfc3..07d37a28905e7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts @@ -25,44 +25,49 @@ export const persistNoteRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.patch( - { + router.versioned + .patch({ path: NOTE_URL, - validate: { - body: buildRouteValidationWithExcess(persistNoteSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(persistNoteSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const { note } = request.body; - const noteId = request.body?.noteId ?? null; + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { note } = request.body; + const noteId = request.body?.noteId ?? null; - const res = await persistNote({ - request: frameworkRequest, - noteId, - note: { - ...note, - timelineId: note.timelineId || null, - }, - overrideOwner: true, - }); + const res = await persistNote({ + request: frameworkRequest, + noteId, + note: { + ...note, + timelineId: note.timelineId || null, + }, + overrideOwner: true, + }); - return response.ok({ - body: { data: { persistNote: res } }, - }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return response.ok({ + body: { data: { persistNote: res } }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts index 8e68010867d8d..529f5d830e803 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts @@ -25,42 +25,47 @@ export const persistPinnedEventRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.patch( - { + router.versioned + .patch({ path: PINNED_EVENT_URL, - validate: { - body: buildRouteValidationWithExcess(persistPinnedEventSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(persistPinnedEventSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const { eventId } = request.body; - const pinnedEventId = request.body?.pinnedEventId ?? null; - const timelineId = request.body?.timelineId ?? null; + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { eventId } = request.body; + const pinnedEventId = request.body?.pinnedEventId ?? null; + const timelineId = request.body?.timelineId ?? null; - const res = await persistPinnedEventOnTimeline( - frameworkRequest, - pinnedEventId, - eventId, - timelineId - ); + const res = await persistPinnedEventOnTimeline( + frameworkRequest, + pinnedEventId, + eventId, + timelineId + ); - return response.ok({ - body: { data: { persistPinnedEventOnTimeline: res } }, - }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return response.ok({ + body: { data: { persistPinnedEventOnTimeline: res } }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts index 718257ac89321..88d868968e9bb 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts @@ -30,10 +30,9 @@ export const installPrepackedTimelinesRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.post( - { + router.versioned + .post({ path: `${TIMELINE_PREPACKAGED_URL}`, - validate: {}, options: { tags: ['access:securitySolution'], body: { @@ -41,54 +40,60 @@ export const installPrepackedTimelinesRoute = ( output: 'stream', }, }, - }, - async (context, request, response) => { - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const prepackagedTimelineStatus = await checkTimelinesStatus(frameworkRequest); - const [validatedprepackagedTimelineStatus, prepackagedTimelineStatusError] = validate( - prepackagedTimelineStatus, - checkTimelineStatusRt - ); + access: 'public', + }) + .addVersion( + { + validate: {}, + version: '2023-10-31', + }, + async (context, request, response) => { + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const prepackagedTimelineStatus = await checkTimelinesStatus(frameworkRequest); + const [validatedprepackagedTimelineStatus, prepackagedTimelineStatusError] = validate( + prepackagedTimelineStatus, + checkTimelineStatusRt + ); - if (prepackagedTimelineStatusError != null) { - throw prepackagedTimelineStatusError; - } + if (prepackagedTimelineStatusError != null) { + throw prepackagedTimelineStatusError; + } - const timelinesToInstalled = - validatedprepackagedTimelineStatus?.timelinesToInstall.length ?? 0; - const timelinesNotUpdated = - validatedprepackagedTimelineStatus?.timelinesToUpdate.length ?? 0; - let res = null; + const timelinesToInstalled = + validatedprepackagedTimelineStatus?.timelinesToInstall.length ?? 0; + const timelinesNotUpdated = + validatedprepackagedTimelineStatus?.timelinesToUpdate.length ?? 0; + let res = null; - if (timelinesToInstalled > 0 || timelinesNotUpdated > 0) { - res = await installPrepackagedTimelines( - config.maxTimelineImportExportSize, - frameworkRequest, - true - ); - } - if (res instanceof Error) { - throw res; - } else { - return response.ok({ - body: res ?? { - success: true, - success_count: 0, - timelines_installed: 0, - timelines_updated: 0, - errors: [], - }, + if (timelinesToInstalled > 0 || timelinesNotUpdated > 0) { + res = await installPrepackagedTimelines( + config.maxTimelineImportExportSize, + frameworkRequest, + true + ); + } + if (res instanceof Error) { + throw res; + } else { + return response.ok({ + body: res ?? { + success: true, + success_count: 0, + timelines_installed: 0, + timelines_updated: 0, + errors: [], + }, + }); + } + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, }); } - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts index 2fe44d49bb84b..91b191c6ead0f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts @@ -34,71 +34,78 @@ export const createTimelinesRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.post( - { + router.versioned + .post({ path: TIMELINE_URL, - validate: { - body: buildRouteValidationWithExcess(createTimelineSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response): Promise> => { - const siemResponse = buildSiemResponse(response); - - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - - const { timelineId, timeline, version } = request.body; - const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = - timeline; - const compareTimelinesStatus = new CompareTimelinesStatus({ - status, - title, - timelineType, - timelineInput: { - id: timelineId, - version, + access: 'public', + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: { + body: buildRouteValidationWithExcess(createTimelineSchema), }, - templateTimelineInput: { - id: templateTimelineId, - version: templateTimelineVersion, - }, - frameworkRequest, - }); - await compareTimelinesStatus.init(); + }, + }, + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); - // Create timeline - if (compareTimelinesStatus.isCreatable) { - const newTimeline = await createTimelines({ + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + + const { timelineId, timeline, version } = request.body; + const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = + timeline; + const compareTimelinesStatus = new CompareTimelinesStatus({ + status, + title, + timelineType, + timelineInput: { + id: timelineId, + version, + }, + templateTimelineInput: { + id: templateTimelineId, + version: templateTimelineVersion, + }, frameworkRequest, - timeline, - timelineVersion: version, }); + await compareTimelinesStatus.init(); - return response.ok({ - body: { - data: { - persistTimeline: newTimeline, + // Create timeline + if (compareTimelinesStatus.isCreatable) { + const newTimeline = await createTimelines({ + frameworkRequest, + timeline, + timelineVersion: version, + }); + + return response.ok({ + body: { + data: { + persistTimeline: newTimeline, + }, }, - }, + }); + } else { + return siemResponse.error( + compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.create) || { + statusCode: 405, + body: DEFAULT_ERROR, + } + ); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, }); - } else { - return siemResponse.error( - compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.create) || { - statusCode: 405, - body: DEFAULT_ERROR, - } - ); } - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts index 12243c6777cae..c9a515f5566c2 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts @@ -22,32 +22,37 @@ export const deleteTimelinesRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.delete( - { + router.versioned + .delete({ path: TIMELINE_URL, - validate: { - body: buildRouteValidationWithExcess(deleteTimelinesSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: { body: buildRouteValidationWithExcess(deleteTimelinesSchema) }, + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const { savedObjectIds } = request.body; + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { savedObjectIds } = request.body; - await deleteTimeline(frameworkRequest, savedObjectIds); - return response.ok({ body: { data: { deleteTimeline: true } } }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + await deleteTimeline(frameworkRequest, savedObjectIds); + return response.ok({ body: { data: { deleteTimeline: true } } }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts index 6c33f81b0c858..6f02c312b7bc7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts @@ -28,52 +28,59 @@ export const exportTimelinesRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.post( - { + router.versioned + .post({ path: TIMELINE_EXPORT_URL, - validate: { - query: buildRouteValidationWithExcess(exportTimelinesQuerySchema), - body: buildRouteValidationWithExcess(exportTimelinesRequestBodySchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - try { - const siemResponse = buildSiemResponse(response); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + access: 'public', + }) + .addVersion( + { + validate: { + request: { + query: buildRouteValidationWithExcess(exportTimelinesQuerySchema), + body: buildRouteValidationWithExcess(exportTimelinesRequestBodySchema), + }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + try { + const siemResponse = buildSiemResponse(response); + const frameworkRequest = await buildFrameworkRequest(context, security, request); - const exportSizeLimit = config.maxTimelineImportExportSize; + const exportSizeLimit = config.maxTimelineImportExportSize; - if (request.body?.ids != null && request.body.ids.length > exportSizeLimit) { - return siemResponse.error({ - statusCode: 400, - body: `Can't export more than ${exportSizeLimit} timelines`, - }); - } + if (request.body?.ids != null && request.body.ids.length > exportSizeLimit) { + return siemResponse.error({ + statusCode: 400, + body: `Can't export more than ${exportSizeLimit} timelines`, + }); + } - const responseBody = await getExportTimelineByObjectIds({ - frameworkRequest, - ids: request.body?.ids, - }); + const responseBody = await getExportTimelineByObjectIds({ + frameworkRequest, + ids: request.body?.ids, + }); - return response.ok({ - headers: { - 'Content-Disposition': `attachment; filename="${request.query.file_name}"`, - 'Content-Type': 'application/ndjson', - }, - body: responseBody, - }); - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); + return response.ok({ + headers: { + 'Content-Disposition': `attachment; filename="${request.query.file_name}"`, + 'Content-Type': 'application/ndjson', + }, + body: responseBody, + }); + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts index 54a44c3e5b4de..25cf3c895f291 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts @@ -29,42 +29,47 @@ export const getTimelineRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.get( - { + router.versioned + .get({ path: TIMELINE_URL, - validate: { - query: buildRouteValidationWithExcess(getTimelineQuerySchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const query = request.query ?? {}; - const { template_timeline_id: templateTimelineId, id } = query; + access: 'public', + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: { query: buildRouteValidationWithExcess(getTimelineQuerySchema) }, + }, + }, + async (context, request, response) => { + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const query = request.query ?? {}; + const { template_timeline_id: templateTimelineId, id } = query; - let res: TimelineSavedObject | ResolvedTimelineWithOutcomeSavedObject | null = null; + let res: TimelineSavedObject | ResolvedTimelineWithOutcomeSavedObject | null = null; - if (templateTimelineId != null && id == null) { - res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); - } else if (templateTimelineId == null && id != null) { - res = await getTimelineOrNull(frameworkRequest, id); - } else { - throw new Error('please provide id or template_timeline_id'); - } + if (templateTimelineId != null && id == null) { + res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); + } else if (templateTimelineId == null && id != null) { + res = await getTimelineOrNull(frameworkRequest, id); + } else { + throw new Error('please provide id or template_timeline_id'); + } - return response.ok({ body: res ? { data: { getOneTimeline: res } } : {} }); - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); + return response.ok({ body: res ? { data: { getOneTimeline: res } } : {} }); + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts index 1abd8270f3374..02cb8ff41f1a3 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts @@ -28,78 +28,84 @@ export const getTimelinesRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.get( - { + router.versioned + .get({ path: TIMELINES_URL, - validate: { - query: escapeHatch, - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const customHttpRequestError = (message: string) => new CustomHttpRequestError(message, 400); - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const queryParams = pipe( - getTimelinesQuerySchema.decode(request.query), - fold(throwErrors(customHttpRequestError), identity) - ); - const onlyUserFavorite = queryParams?.only_user_favorite === 'true' ? true : false; - const pageSize = queryParams?.page_size ? parseInt(queryParams.page_size, 10) : null; - const pageIndex = queryParams?.page_index ? parseInt(queryParams.page_index, 10) : null; - const search = queryParams?.search ?? null; - const sortField = queryParams?.sort_field ?? null; - const sortOrder = queryParams?.sort_order ?? null; - const status = queryParams?.status ?? null; - const timelineType = queryParams?.timeline_type ?? null; - const sort = - sortField && sortOrder - ? { - sortField, - sortOrder, - } - : null; - let res = null; - let totalCount = null; + access: 'public', + }) + .addVersion( + { + validate: { + request: { query: escapeHatch }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + const customHttpRequestError = (message: string) => + new CustomHttpRequestError(message, 400); + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const queryParams = pipe( + getTimelinesQuerySchema.decode(request.query), + fold(throwErrors(customHttpRequestError), identity) + ); + const onlyUserFavorite = queryParams?.only_user_favorite === 'true' ? true : false; + const pageSize = queryParams?.page_size ? parseInt(queryParams.page_size, 10) : null; + const pageIndex = queryParams?.page_index ? parseInt(queryParams.page_index, 10) : null; + const search = queryParams?.search ?? null; + const sortField = queryParams?.sort_field ?? null; + const sortOrder = queryParams?.sort_order ?? null; + const status = queryParams?.status ?? null; + const timelineType = queryParams?.timeline_type ?? null; + const sort = + sortField && sortOrder + ? { + sortField, + sortOrder, + } + : null; + let res = null; + let totalCount = null; + + if (pageSize == null && pageIndex == null) { + const allActiveTimelines = await getAllTimeline( + frameworkRequest, + false, + { pageSize: 1, pageIndex: 1 }, + null, + null, + null, + null + ); + totalCount = allActiveTimelines.totalCount; + } - if (pageSize == null && pageIndex == null) { - const allActiveTimelines = await getAllTimeline( + res = await getAllTimeline( frameworkRequest, - false, - { pageSize: 1, pageIndex: 1 }, - null, - null, - null, - null + onlyUserFavorite, + { + pageSize: pageSize ?? totalCount ?? 1, + pageIndex: pageIndex ?? 1, + }, + search, + sort, + status, + timelineType ); - totalCount = allActiveTimelines.totalCount; - } - - res = await getAllTimeline( - frameworkRequest, - onlyUserFavorite, - { - pageSize: pageSize ?? totalCount ?? 1, - pageIndex: pageIndex ?? 1, - }, - search, - sort, - status, - timelineType - ); - return response.ok({ body: res ?? {} }); - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); + return response.ok({ body: res ?? {} }); + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts index 11755868b7336..8d6326af6c17c 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts @@ -29,12 +29,9 @@ export const importTimelinesRoute = ( config: ConfigType, security: SetupPlugins['security'] ) => { - router.post( - { + router.versioned + .post({ path: `${TIMELINE_IMPORT_URL}`, - validate: { - body: buildRouteValidationWithExcess(ImportTimelinesPayloadSchemaRt), - }, options: { tags: ['access:securitySolution'], body: { @@ -42,43 +39,51 @@ export const importTimelinesRoute = ( output: 'stream', }, }, - }, - async (context, request, response) => { - try { - const siemResponse = buildSiemResponse(response); - const savedObjectsClient = (await context.core).savedObjects.client; - if (!savedObjectsClient) { - return siemResponse.error({ statusCode: 404 }); - } + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(ImportTimelinesPayloadSchemaRt) }, + }, + version: '2023-10-31', + }, + async (context, request, response) => { + try { + const siemResponse = buildSiemResponse(response); + const savedObjectsClient = (await context.core).savedObjects.client; + if (!savedObjectsClient) { + return siemResponse.error({ statusCode: 404 }); + } + + const { file, isImmutable } = request.body; + const { filename } = file.hapi; + const fileExtension = extname(filename).toLowerCase(); - const { file, isImmutable } = request.body; - const { filename } = file.hapi; - const fileExtension = extname(filename).toLowerCase(); + if (fileExtension !== '.ndjson') { + return siemResponse.error({ + statusCode: 400, + body: `Invalid file extension ${fileExtension}`, + }); + } + const frameworkRequest = await buildFrameworkRequest(context, security, request); - if (fileExtension !== '.ndjson') { + const res = await importTimelines( + file as unknown as Readable, + config.maxTimelineImportExportSize, + frameworkRequest, + isImmutable === 'true' + ); + if (typeof res !== 'string') return response.ok({ body: res ?? {} }); + else throw res; + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); return siemResponse.error({ - statusCode: 400, - body: `Invalid file extension ${fileExtension}`, + body: error.message, + statusCode: error.statusCode, }); } - const frameworkRequest = await buildFrameworkRequest(context, security, request); - - const res = await importTimelines( - file as unknown as Readable, - config.maxTimelineImportExportSize, - frameworkRequest, - isImmutable === 'true' - ); - if (typeof res !== 'string') return response.ok({ body: res ?? {} }); - else throw res; - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts index a213a67b1f835..c8b9c26536444 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts @@ -28,72 +28,77 @@ export const patchTimelinesRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.patch( - { + router.versioned + .patch({ path: TIMELINE_URL, - validate: { - body: buildRouteValidationWithExcess(patchTimelineSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response): Promise> => { - const siemResponse = buildSiemResponse(response); - - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const { timelineId, timeline, version } = request.body; - const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = - timeline; + access: 'public', + }) + .addVersion( + { + validate: { + request: { body: buildRouteValidationWithExcess(patchTimelineSchema) }, + }, + version: '2023-10-31', + }, + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); - const compareTimelinesStatus = new CompareTimelinesStatus({ - status, - title, - timelineType, - timelineInput: { - id: timelineId, - version, - }, - templateTimelineInput: { - id: templateTimelineId, - version: templateTimelineVersion, - }, - frameworkRequest, - }); + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { timelineId, timeline, version } = request.body; + const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = + timeline; - await compareTimelinesStatus.init(); - if (compareTimelinesStatus.isUpdatable) { - const updatedTimeline = await createTimelines({ + const compareTimelinesStatus = new CompareTimelinesStatus({ + status, + title, + timelineType, + timelineInput: { + id: timelineId, + version, + }, + templateTimelineInput: { + id: templateTimelineId, + version: templateTimelineVersion, + }, frameworkRequest, - timeline, - timelineSavedObjectId: timelineId, - timelineVersion: version, }); - return response.ok({ - body: { - data: { - persistTimeline: updatedTimeline, + await compareTimelinesStatus.init(); + if (compareTimelinesStatus.isUpdatable) { + const updatedTimeline = await createTimelines({ + frameworkRequest, + timeline, + timelineSavedObjectId: timelineId, + timelineVersion: version, + }); + + return response.ok({ + body: { + data: { + persistTimeline: updatedTimeline, + }, }, - }, + }); + } else { + const error = compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.update); + return siemResponse.error( + error || { + statusCode: 405, + body: 'update timeline error', + } + ); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, }); - } else { - const error = compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.update); - return siemResponse.error( - error || { - statusCode: 405, - body: 'update timeline error', - } - ); } - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts index 70dc5ee2449b5..746987caa3759 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts @@ -25,46 +25,51 @@ export const persistFavoriteRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.patch( - { + router.versioned + .patch({ path: TIMELINE_FAVORITE_URL, - validate: { - body: buildRouteValidationWithExcess(persistFavoriteSchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - const siemResponse = buildSiemResponse(response); + access: 'public', + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: { body: buildRouteValidationWithExcess(persistFavoriteSchema) }, + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const { timelineId, templateTimelineId, templateTimelineVersion, timelineType } = - request.body; + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { timelineId, templateTimelineId, templateTimelineVersion, timelineType } = + request.body; - const timeline = await persistFavorite( - frameworkRequest, - timelineId || null, - templateTimelineId || null, - templateTimelineVersion || null, - timelineType || TimelineType.default - ); + const timeline = await persistFavorite( + frameworkRequest, + timelineId || null, + templateTimelineId || null, + templateTimelineVersion || null, + timelineType || TimelineType.default + ); - return response.ok({ - body: { - data: { - persistFavorite: timeline, + return response.ok({ + body: { + data: { + persistFavorite: timeline, + }, }, - }, - }); - } catch (err) { - const error = transformError(err); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts index 75ba2f4ec62cb..7572e72cfb9ac 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts @@ -29,44 +29,49 @@ export const resolveTimelineRoute = ( _: ConfigType, security: SetupPlugins['security'] ) => { - router.get( - { + router.versioned + .get({ path: TIMELINE_RESOLVE_URL, - validate: { - query: buildRouteValidationWithExcess(getTimelineQuerySchema), - }, options: { tags: ['access:securitySolution'], }, - }, - async (context, request, response) => { - try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); - const query = request.query ?? {}; - const { template_timeline_id: templateTimelineId, id } = query; + access: 'public', + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: { query: buildRouteValidationWithExcess(getTimelineQuerySchema) }, + }, + }, + async (context, request, response) => { + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const query = request.query ?? {}; + const { template_timeline_id: templateTimelineId, id } = query; - let res: SavedTimeline | ResolvedTimelineWithOutcomeSavedObject | null = null; + let res: SavedTimeline | ResolvedTimelineWithOutcomeSavedObject | null = null; - if (templateTimelineId != null && id == null) { - // Template timelineId is not a SO id, so it does not need to be updated to use resolve - res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); - } else if (templateTimelineId == null && id != null) { - // In the event the objectId is defined, run the resolve call - res = await resolveTimelineOrNull(frameworkRequest, id); - } else { - throw new Error('please provide id or template_timeline_id'); - } + if (templateTimelineId != null && id == null) { + // Template timelineId is not a SO id, so it does not need to be updated to use resolve + res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); + } else if (templateTimelineId == null && id != null) { + // In the event the objectId is defined, run the resolve call + res = await resolveTimelineOrNull(frameworkRequest, id); + } else { + throw new Error('please provide id or template_timeline_id'); + } - return response.ok({ body: res ? { data: res } : {} }); - } catch (err) { - const error = transformError(err); - const siemResponse = buildSiemResponse(response); + return response.ok({ body: res ? { data: res } : {} }); + } catch (err) { + const error = transformError(err); + const siemResponse = buildSiemResponse(response); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } } - } - ); + ); };