diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 0581d7a850c68..c858823e2ae0a 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -31617,13 +31617,6 @@ paths: required: true responses: '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - type: object - properties: - data: - type: object description: Indicates the note was successfully deleted. summary: Delete a note tags: @@ -31685,9 +31678,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - $ref: '#/components/schemas/Security_Timeline_API_GetNotesResult' - - type: object + $ref: '#/components/schemas/Security_Timeline_API_GetNotesResult' description: Indicates the requested notes were returned. summary: Get notes tags: @@ -31731,17 +31722,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistNote: - $ref: '#/components/schemas/Security_Timeline_API_ResponseNote' - required: - - persistNote - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_ResponseNote' description: Indicates the note was successfully created. summary: Add or update a note tags: @@ -32094,17 +32075,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistPinnedEventOnTimeline: - $ref: '#/components/schemas/Security_Timeline_API_PersistPinnedEventResponse' - required: - - persistPinnedEventOnTimeline - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_PersistPinnedEventResponse' description: Indicates the event was successfully pinned to the Timeline. summary: Pin an event tags: @@ -33715,20 +33686,6 @@ paths: required: true responses: '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - type: object - properties: - data: - type: object - properties: - deleteTimeline: - type: boolean - required: - - deleteTimeline - required: - - data description: Indicates the Timeline was successfully deleted. summary: Delete Timelines or Timeline templates tags: @@ -33753,20 +33710,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - type: object - properties: - data: - type: object - properties: - getOneTimeline: - $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' - required: - - getOneTimeline - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' description: Indicates that the (template) Timeline was found and returned. summary: Get Timeline or Timeline template details tags: @@ -34077,17 +34021,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistFavorite: - $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResponse' - required: - - persistFavorite - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResponse' description: Indicates the favorite status was successfully updated. '403': content: @@ -34244,15 +34178,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - type: object - properties: - data: - $ref: '#/components/schemas/Security_Timeline_API_ResolvedTimeline' - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/Security_Timeline_API_ResolvedTimeline' description: The (template) Timeline has been found '400': description: The request is missing parameters @@ -47290,16 +47216,10 @@ components: Security_Timeline_API_FavoriteTimelineResponse: type: object properties: - code: - nullable: true - type: number favorite: items: $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResult' type: array - message: - nullable: true - type: string savedObjectId: type: string templateTimelineId: @@ -47468,28 +47388,15 @@ components: - version Security_Timeline_API_PersistPinnedEventResponse: oneOf: - - allOf: - - $ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' - - $ref: '#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody' - - nullable: true - type: object - Security_Timeline_API_PersistTimelineResponse: - type: object - properties: - data: - type: object + - $ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' + - type: object properties: - persistTimeline: - type: object - properties: - timeline: - $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' - required: - - timeline + unpinned: + type: boolean required: - - persistTimeline - required: - - data + - unpinned + Security_Timeline_API_PersistTimelineResponse: + $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' Security_Timeline_API_PinnedEvent: allOf: - $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent' @@ -47502,15 +47409,6 @@ components: required: - pinnedEventId - version - Security_Timeline_API_PinnedEventBaseResponseBody: - type: object - properties: - code: - type: number - message: - type: string - required: - - code Security_Timeline_API_QueryMatchResult: type: object properties: @@ -47551,15 +47449,9 @@ components: Security_Timeline_API_ResponseNote: type: object properties: - code: - type: number - message: - type: string note: $ref: '#/components/schemas/Security_Timeline_API_Note' required: - - code - - message - note Security_Timeline_API_RowRendererId: enum: diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 322160b6231cc..6780ef4926c73 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -34361,13 +34361,6 @@ paths: required: true responses: '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - type: object - properties: - data: - type: object description: Indicates the note was successfully deleted. summary: Delete a note tags: @@ -34428,9 +34421,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - $ref: '#/components/schemas/Security_Timeline_API_GetNotesResult' - - type: object + $ref: '#/components/schemas/Security_Timeline_API_GetNotesResult' description: Indicates the requested notes were returned. summary: Get notes tags: @@ -34473,17 +34464,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistNote: - $ref: '#/components/schemas/Security_Timeline_API_ResponseNote' - required: - - persistNote - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_ResponseNote' description: Indicates the note was successfully created. summary: Add or update a note tags: @@ -34821,17 +34802,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistPinnedEventOnTimeline: - $ref: '#/components/schemas/Security_Timeline_API_PersistPinnedEventResponse' - required: - - persistPinnedEventOnTimeline - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_PersistPinnedEventResponse' description: Indicates the event was successfully pinned to the Timeline. summary: Pin an event tags: @@ -37370,20 +37341,6 @@ paths: required: true responses: '200': - content: - application/json; Elastic-Api-Version=2023-10-31: - schema: - type: object - properties: - data: - type: object - properties: - deleteTimeline: - type: boolean - required: - - deleteTimeline - required: - - data description: Indicates the Timeline was successfully deleted. summary: Delete Timelines or Timeline templates tags: @@ -37407,20 +37364,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - type: object - properties: - data: - type: object - properties: - getOneTimeline: - $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' - required: - - getOneTimeline - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' description: Indicates that the (template) Timeline was found and returned. summary: Get Timeline or Timeline template details tags: @@ -37724,17 +37668,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - type: object - properties: - data: - type: object - properties: - persistFavorite: - $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResponse' - required: - - persistFavorite - required: - - data + $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResponse' description: Indicates the favorite status was successfully updated. '403': content: @@ -37888,15 +37822,7 @@ paths: content: application/json; Elastic-Api-Version=2023-10-31: schema: - oneOf: - - type: object - properties: - data: - $ref: '#/components/schemas/Security_Timeline_API_ResolvedTimeline' - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/Security_Timeline_API_ResolvedTimeline' description: The (template) Timeline has been found '400': description: The request is missing parameters @@ -55013,16 +54939,10 @@ components: Security_Timeline_API_FavoriteTimelineResponse: type: object properties: - code: - nullable: true - type: number favorite: items: $ref: '#/components/schemas/Security_Timeline_API_FavoriteTimelineResult' type: array - message: - nullable: true - type: string savedObjectId: type: string templateTimelineId: @@ -55191,28 +55111,15 @@ components: - version Security_Timeline_API_PersistPinnedEventResponse: oneOf: - - allOf: - - $ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' - - $ref: '#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody' - - nullable: true - type: object - Security_Timeline_API_PersistTimelineResponse: - type: object - properties: - data: - type: object + - $ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' + - type: object properties: - persistTimeline: - type: object - properties: - timeline: - $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' - required: - - timeline + unpinned: + type: boolean required: - - persistTimeline - required: - - data + - unpinned + Security_Timeline_API_PersistTimelineResponse: + $ref: '#/components/schemas/Security_Timeline_API_TimelineResponse' Security_Timeline_API_PinnedEvent: allOf: - $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent' @@ -55225,15 +55132,6 @@ components: required: - pinnedEventId - version - Security_Timeline_API_PinnedEventBaseResponseBody: - type: object - properties: - code: - type: number - message: - type: string - required: - - code Security_Timeline_API_QueryMatchResult: type: object properties: @@ -55274,15 +55172,9 @@ components: Security_Timeline_API_ResponseNote: type: object properties: - code: - type: number - message: - type: string note: $ref: '#/components/schemas/Security_Timeline_API_Note' required: - - code - - message - note Security_Timeline_API_RowRendererId: enum: diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index f0d8445c41eed..5a1cf49baecd1 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -299,14 +299,8 @@ import type { CreateTimelinesRequestBodyInput, CreateTimelinesResponse, } from './timeline/create_timelines/create_timelines_route.gen'; -import type { - DeleteNoteRequestBodyInput, - DeleteNoteResponse, -} from './timeline/delete_note/delete_note_route.gen'; -import type { - DeleteTimelinesRequestBodyInput, - DeleteTimelinesResponse, -} from './timeline/delete_timelines/delete_timelines_route.gen'; +import type { DeleteNoteRequestBodyInput } from './timeline/delete_note/delete_note_route.gen'; +import type { DeleteTimelinesRequestBodyInput } from './timeline/delete_timelines/delete_timelines_route.gen'; import type { ExportTimelinesRequestQueryInput, ExportTimelinesRequestBodyInput, @@ -768,7 +762,7 @@ If a record already exists for the specified entity, that record is overwritten async deleteNote(props: DeleteNoteProps) { this.log.info(`${new Date().toISOString()} Calling API DeleteNote`); return this.kbnClient - .request({ + .request({ path: '/api/note', headers: { [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', @@ -801,7 +795,7 @@ If a record already exists for the specified entity, that record is overwritten async deleteTimelines(props: DeleteTimelinesProps) { this.log.info(`${new Date().toISOString()} Calling API DeleteTimelines`); return this.kbnClient - .request({ + .request({ path: '/api/timeline', headers: { [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', diff --git a/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.gen.ts index d98455c1fdb59..f0ee5665b9f78 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.gen.ts @@ -30,8 +30,3 @@ export const DeleteNoteRequestBody = z.union([ .nullable(), ]); export type DeleteNoteRequestBodyInput = z.input; - -export type DeleteNoteResponse = z.infer; -export const DeleteNoteResponse = z.object({ - data: z.object({}).optional(), -}); diff --git a/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.schema.yaml index e79cb9aab65ac..672488e4ff6cd 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/delete_note/delete_note_route.schema.yaml @@ -37,10 +37,3 @@ paths: responses: '200': description: Indicates the note was successfully deleted. - content: - application/json: - schema: - type: object - properties: - data: - type: object diff --git a/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.gen.ts index 5b63249843f41..98761d710f031 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.gen.ts @@ -25,10 +25,3 @@ export const DeleteTimelinesRequestBody = z.object({ searchIds: z.array(z.string()).optional(), }); export type DeleteTimelinesRequestBodyInput = z.input; - -export type DeleteTimelinesResponse = z.infer; -export const DeleteTimelinesResponse = z.object({ - data: z.object({ - deleteTimeline: z.boolean(), - }), -}); diff --git a/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.schema.yaml index bb6674fa65877..4dd5105a737a3 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/delete_timelines/delete_timelines_route.schema.yaml @@ -36,15 +36,3 @@ paths: responses: '200': description: Indicates the Timeline was successfully deleted. - content: - application/json: - schema: - type: object - required: [data] - properties: - data: - type: object - required: [deleteTimeline] - properties: - deleteTimeline: - type: boolean diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.gen.ts index 0ee6445dd71e3..c8ef00e3c5260 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.gen.ts @@ -60,4 +60,4 @@ export const GetNotesRequestQuery = z.object({ export type GetNotesRequestQueryInput = z.input; export type GetNotesResponse = z.infer; -export const GetNotesResponse = z.union([GetNotesResult, z.object({})]); +export const GetNotesResponse = GetNotesResult; diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml index e142126817707..601fd8bfc9966 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml @@ -66,9 +66,7 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/GetNotesResult' - - type: object + $ref: '#/components/schemas/GetNotesResult' components: schemas: diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.gen.ts index 7a41788077524..72df3caf8c6d6 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.gen.ts @@ -32,11 +32,4 @@ export const GetTimelineRequestQuery = z.object({ export type GetTimelineRequestQueryInput = z.input; export type GetTimelineResponse = z.infer; -export const GetTimelineResponse = z.union([ - z.object({ - data: z.object({ - getOneTimeline: TimelineResponse, - }), - }), - z.object({}).strict(), -]); +export const GetTimelineResponse = TimelineResponse; diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.schema.yaml index 9b5d3fedfd59e..40022f34b6ea5 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/get_timeline/get_timeline_route.schema.yaml @@ -32,15 +32,4 @@ paths: content: application/json: schema: - oneOf: - - type: object - required: [data] - properties: - data: - type: object - required: [getOneTimeline] - properties: - getOneTimeline: - $ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse' - - type: object - additionalProperties: false + $ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse' diff --git a/x-pack/plugins/security_solution/common/api/timeline/model/components.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/model/components.gen.ts index 93d627f53263b..6de7425d226e8 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/model/components.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/model/components.gen.ts @@ -309,8 +309,6 @@ export type FavoriteTimelineResponse = z.infer; export const FavoriteTimelineResponse = z.object({ savedObjectId: z.string(), version: z.string(), - code: z.number().nullable().optional(), - message: z.string().nullable().optional(), templateTimelineId: z.string().nullable().optional(), templateTimelineVersion: z.number().nullable().optional(), timelineType: TimelineType.optional(), @@ -318,13 +316,7 @@ export const FavoriteTimelineResponse = z.object({ }); export type PersistTimelineResponse = z.infer; -export const PersistTimelineResponse = z.object({ - data: z.object({ - persistTimeline: z.object({ - timeline: TimelineResponse, - }), - }), -}); +export const PersistTimelineResponse = TimelineResponse; export type BareNoteWithoutExternalRefs = z.infer; export const BareNoteWithoutExternalRefs = z.object({ diff --git a/x-pack/plugins/security_solution/common/api/timeline/model/components.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/model/components.schema.yaml index 568eec1975769..fb5b4a964788c 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/model/components.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/model/components.schema.yaml @@ -225,12 +225,6 @@ components: type: string version: type: string - code: - type: number - nullable: true - message: - type: string - nullable: true templateTimelineId: type: string nullable: true @@ -244,19 +238,7 @@ components: items: $ref: '#/components/schemas/FavoriteTimelineResult' PersistTimelineResponse: - type: object - required: [data] - properties: - data: - type: object - required: [persistTimeline] - properties: - persistTimeline: - type: object - required: [timeline] - properties: - timeline: - $ref: '#/components/schemas/TimelineResponse' + $ref: '#/components/schemas/TimelineResponse' ColumnHeaderResult: type: object properties: diff --git a/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.gen.ts index c3c5f16cf2e32..675ad647c692c 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.gen.ts @@ -28,8 +28,4 @@ export const PersistFavoriteRouteRequestBody = z.object({ export type PersistFavoriteRouteRequestBodyInput = z.input; export type PersistFavoriteRouteResponse = z.infer; -export const PersistFavoriteRouteResponse = z.object({ - data: z.object({ - persistFavorite: FavoriteTimelineResponse, - }), -}); +export const PersistFavoriteRouteResponse = FavoriteTimelineResponse; diff --git a/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.schema.yaml index 3a57dd066d3b3..c3a0def405a6c 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/persist_favorite/persist_favorite_route.schema.yaml @@ -39,15 +39,8 @@ paths: content: application/json: schema: - type: object - required: [data] - properties: - data: - type: object - required: [persistFavorite] - properties: - persistFavorite: - $ref: '../model/components.schema.yaml#/components/schemas/FavoriteTimelineResponse' + $ref: '../model/components.schema.yaml#/components/schemas/FavoriteTimelineResponse' + '403': description: Indicates the user does not have the required permissions to persist the favorite status. content: diff --git a/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.gen.ts index 36def713d4994..a24428d64c2b0 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.gen.ts @@ -20,8 +20,6 @@ import { BareNote, Note } from '../model/components.gen'; export type ResponseNote = z.infer; export const ResponseNote = z.object({ - code: z.number(), - message: z.string(), note: Note, }); @@ -38,8 +36,4 @@ export const PersistNoteRouteRequestBody = z.object({ export type PersistNoteRouteRequestBodyInput = z.input; export type PersistNoteRouteResponse = z.infer; -export const PersistNoteRouteResponse = z.object({ - data: z.object({ - persistNote: ResponseNote, - }), -}); +export const PersistNoteRouteResponse = ResponseNote; diff --git a/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.schema.yaml index 640e75171c613..c5b3cee081667 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/persist_note/persist_note_route.schema.yaml @@ -50,24 +50,12 @@ paths: content: application/json: schema: - type: object - required: [data] - properties: - data: - type: object - required: [persistNote] - properties: - persistNote: - $ref: '#/components/schemas/ResponseNote' + $ref: '#/components/schemas/ResponseNote' components: schemas: ResponseNote: type: object - required: [code, message, note] + required: [note] properties: - code: - type: number - message: - type: string note: $ref: '../model/components.schema.yaml#/components/schemas/Note' diff --git a/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.gen.ts index 6fd628e5a258e..8dd99913c5e30 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.gen.ts @@ -18,16 +18,12 @@ import { z } from '@kbn/zod'; import { PinnedEvent } from '../model/components.gen'; -export type PinnedEventBaseResponseBody = z.infer; -export const PinnedEventBaseResponseBody = z.object({ - code: z.number(), - message: z.string().optional(), -}); - export type PersistPinnedEventResponse = z.infer; export const PersistPinnedEventResponse = z.union([ - PinnedEvent.merge(PinnedEventBaseResponseBody), - z.object({}).nullable(), + PinnedEvent, + z.object({ + unpinned: z.boolean(), + }), ]); export type PersistPinnedEventRouteRequestBody = z.infer; @@ -41,8 +37,4 @@ export type PersistPinnedEventRouteRequestBodyInput = z.input< >; export type PersistPinnedEventRouteResponse = z.infer; -export const PersistPinnedEventRouteResponse = z.object({ - data: z.object({ - persistPinnedEventOnTimeline: PersistPinnedEventResponse, - }), -}); +export const PersistPinnedEventRouteResponse = PersistPinnedEventResponse; diff --git a/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.schema.yaml index 3b697e957ad89..3059dec003312 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/pinned_events/pinned_events_route.schema.yaml @@ -37,30 +37,15 @@ paths: content: application/json: schema: - type: object - required: [data] - properties: - data: - type: object - required: [persistPinnedEventOnTimeline] - properties: - persistPinnedEventOnTimeline: - $ref: '#/components/schemas/PersistPinnedEventResponse' + $ref: '#/components/schemas/PersistPinnedEventResponse' components: schemas: PersistPinnedEventResponse: oneOf: - - allOf: - - $ref: '../model/components.schema.yaml#/components/schemas/PinnedEvent' - - $ref: '#/components/schemas/PinnedEventBaseResponseBody' + - $ref: '../model/components.schema.yaml#/components/schemas/PinnedEvent' - type: object - nullable: true - PinnedEventBaseResponseBody: - type: object - required: [code] - properties: - code: - type: number - message: - type: string + required: [unpinned] + properties: + unpinned: + type: boolean diff --git a/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.gen.ts b/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.gen.ts index d4c79eec50b26..d245dcf4a16ab 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.gen.ts @@ -32,9 +32,4 @@ export const ResolveTimelineRequestQuery = z.object({ export type ResolveTimelineRequestQueryInput = z.input; export type ResolveTimelineResponse = z.infer; -export const ResolveTimelineResponse = z.union([ - z.object({ - data: ResolvedTimeline, - }), - z.object({}).strict(), -]); +export const ResolveTimelineResponse = ResolvedTimeline; diff --git a/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.schema.yaml index b06969e28cad4..ce0cfdc81f527 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/resolve_timeline/resolve_timeline_route.schema.yaml @@ -28,14 +28,7 @@ paths: content: application/json: schema: - oneOf: - - type: object - required: [data] - properties: - data: - $ref: '../model/components.schema.yaml#/components/schemas/ResolvedTimeline' - - type: object - additionalProperties: false + $ref: '../model/components.schema.yaml#/components/schemas/ResolvedTimeline' '400': description: The request is missing parameters diff --git a/x-pack/plugins/security_solution/common/api/timeline/routes.ts b/x-pack/plugins/security_solution/common/api/timeline/routes.ts index 70b339c92f197..d5996d9706701 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/routes.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/routes.ts @@ -5,17 +5,14 @@ * 2.0. */ -export { - DeleteTimelinesRequestBody, - DeleteTimelinesResponse, -} from './delete_timelines/delete_timelines_route.gen'; +export { DeleteTimelinesRequestBody } from './delete_timelines/delete_timelines_route.gen'; export { PersistNoteRouteRequestBody, PersistNoteRouteResponse, ResponseNote, } from './persist_note/persist_note_route.gen'; -export { DeleteNoteRequestBody, DeleteNoteResponse } from './delete_note/delete_note_route.gen'; +export { DeleteNoteRequestBody } from './delete_note/delete_note_route.gen'; export { CleanDraftTimelinesResponse, diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml index 562bf9b80d3ea..351e53f2fd013 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml @@ -43,13 +43,6 @@ paths: required: true responses: '200': - content: - application/json: - schema: - type: object - properties: - data: - type: object description: Indicates the note was successfully deleted. summary: Delete a note tags: @@ -111,9 +104,7 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/GetNotesResult' - - type: object + $ref: '#/components/schemas/GetNotesResult' description: Indicates the requested notes were returned. summary: Get notes tags: @@ -157,17 +148,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistNote: - $ref: '#/components/schemas/ResponseNote' - required: - - persistNote - required: - - data + $ref: '#/components/schemas/ResponseNote' description: Indicates the note was successfully created. summary: Add or update a note tags: @@ -200,17 +181,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistPinnedEventOnTimeline: - $ref: '#/components/schemas/PersistPinnedEventResponse' - required: - - persistPinnedEventOnTimeline - required: - - data + $ref: '#/components/schemas/PersistPinnedEventResponse' description: Indicates the event was successfully pinned to the Timeline. summary: Pin an event tags: @@ -243,20 +214,6 @@ paths: required: true responses: '200': - content: - application/json: - schema: - type: object - properties: - data: - type: object - properties: - deleteTimeline: - type: boolean - required: - - deleteTimeline - required: - - data description: Indicates the Timeline was successfully deleted. summary: Delete Timelines or Timeline templates tags: @@ -281,20 +238,7 @@ paths: content: application/json: schema: - oneOf: - - type: object - properties: - data: - type: object - properties: - getOneTimeline: - $ref: '#/components/schemas/TimelineResponse' - required: - - getOneTimeline - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/TimelineResponse' description: Indicates that the (template) Timeline was found and returned. summary: Get Timeline or Timeline template details tags: @@ -636,17 +580,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistFavorite: - $ref: '#/components/schemas/FavoriteTimelineResponse' - required: - - persistFavorite - required: - - data + $ref: '#/components/schemas/FavoriteTimelineResponse' description: Indicates the favorite status was successfully updated. '403': content: @@ -811,15 +745,7 @@ paths: content: application/json: schema: - oneOf: - - type: object - properties: - data: - $ref: '#/components/schemas/ResolvedTimeline' - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/ResolvedTimeline' description: The (template) Timeline has been found '400': description: The request is missing parameters @@ -1089,16 +1015,10 @@ components: FavoriteTimelineResponse: type: object properties: - code: - nullable: true - type: number favorite: items: $ref: '#/components/schemas/FavoriteTimelineResult' type: array - message: - nullable: true - type: string savedObjectId: type: string templateTimelineId: @@ -1267,28 +1187,15 @@ components: - version PersistPinnedEventResponse: oneOf: - - allOf: - - $ref: '#/components/schemas/PinnedEvent' - - $ref: '#/components/schemas/PinnedEventBaseResponseBody' - - nullable: true - type: object - PersistTimelineResponse: - type: object - properties: - data: - type: object + - $ref: '#/components/schemas/PinnedEvent' + - type: object properties: - persistTimeline: - type: object - properties: - timeline: - $ref: '#/components/schemas/TimelineResponse' - required: - - timeline + unpinned: + type: boolean required: - - persistTimeline - required: - - data + - unpinned + PersistTimelineResponse: + $ref: '#/components/schemas/TimelineResponse' PinnedEvent: allOf: - $ref: '#/components/schemas/BarePinnedEvent' @@ -1301,15 +1208,6 @@ components: required: - pinnedEventId - version - PinnedEventBaseResponseBody: - type: object - properties: - code: - type: number - message: - type: string - required: - - code QueryMatchResult: type: object properties: @@ -1350,15 +1248,9 @@ components: ResponseNote: type: object properties: - code: - type: number - message: - type: string note: $ref: '#/components/schemas/Note' required: - - code - - message - note RowRendererId: enum: diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml index a68919aa0e1fd..410dd9962f409 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml @@ -43,13 +43,6 @@ paths: required: true responses: '200': - content: - application/json: - schema: - type: object - properties: - data: - type: object description: Indicates the note was successfully deleted. summary: Delete a note tags: @@ -111,9 +104,7 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/GetNotesResult' - - type: object + $ref: '#/components/schemas/GetNotesResult' description: Indicates the requested notes were returned. summary: Get notes tags: @@ -157,17 +148,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistNote: - $ref: '#/components/schemas/ResponseNote' - required: - - persistNote - required: - - data + $ref: '#/components/schemas/ResponseNote' description: Indicates the note was successfully created. summary: Add or update a note tags: @@ -200,17 +181,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistPinnedEventOnTimeline: - $ref: '#/components/schemas/PersistPinnedEventResponse' - required: - - persistPinnedEventOnTimeline - required: - - data + $ref: '#/components/schemas/PersistPinnedEventResponse' description: Indicates the event was successfully pinned to the Timeline. summary: Pin an event tags: @@ -243,20 +214,6 @@ paths: required: true responses: '200': - content: - application/json: - schema: - type: object - properties: - data: - type: object - properties: - deleteTimeline: - type: boolean - required: - - deleteTimeline - required: - - data description: Indicates the Timeline was successfully deleted. summary: Delete Timelines or Timeline templates tags: @@ -281,20 +238,7 @@ paths: content: application/json: schema: - oneOf: - - type: object - properties: - data: - type: object - properties: - getOneTimeline: - $ref: '#/components/schemas/TimelineResponse' - required: - - getOneTimeline - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/TimelineResponse' description: Indicates that the (template) Timeline was found and returned. summary: Get Timeline or Timeline template details tags: @@ -636,17 +580,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: object - properties: - persistFavorite: - $ref: '#/components/schemas/FavoriteTimelineResponse' - required: - - persistFavorite - required: - - data + $ref: '#/components/schemas/FavoriteTimelineResponse' description: Indicates the favorite status was successfully updated. '403': content: @@ -811,15 +745,7 @@ paths: content: application/json: schema: - oneOf: - - type: object - properties: - data: - $ref: '#/components/schemas/ResolvedTimeline' - required: - - data - - additionalProperties: false - type: object + $ref: '#/components/schemas/ResolvedTimeline' description: The (template) Timeline has been found '400': description: The request is missing parameters @@ -1089,16 +1015,10 @@ components: FavoriteTimelineResponse: type: object properties: - code: - nullable: true - type: number favorite: items: $ref: '#/components/schemas/FavoriteTimelineResult' type: array - message: - nullable: true - type: string savedObjectId: type: string templateTimelineId: @@ -1267,28 +1187,15 @@ components: - version PersistPinnedEventResponse: oneOf: - - allOf: - - $ref: '#/components/schemas/PinnedEvent' - - $ref: '#/components/schemas/PinnedEventBaseResponseBody' - - nullable: true - type: object - PersistTimelineResponse: - type: object - properties: - data: - type: object + - $ref: '#/components/schemas/PinnedEvent' + - type: object properties: - persistTimeline: - type: object - properties: - timeline: - $ref: '#/components/schemas/TimelineResponse' - required: - - timeline + unpinned: + type: boolean required: - - persistTimeline - required: - - data + - unpinned + PersistTimelineResponse: + $ref: '#/components/schemas/TimelineResponse' PinnedEvent: allOf: - $ref: '#/components/schemas/BarePinnedEvent' @@ -1301,15 +1208,6 @@ components: required: - pinnedEventId - version - PinnedEventBaseResponseBody: - type: object - properties: - code: - type: number - message: - type: string - required: - - code QueryMatchResult: type: object properties: @@ -1350,15 +1248,9 @@ components: ResponseNote: type: object properties: - code: - type: number - message: - type: string note: $ref: '#/components/schemas/Note' required: - - code - - message - note RowRendererId: enum: diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 406ff1f4ffe31..7af93ac62803c 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -2015,15 +2015,6 @@ export const mockGetOneTimelineResult: TimelineResponse = { version: '1', }; -export const mockTimelineResult = { - data: { - getOneTimeline: mockGetOneTimelineResult, - }, - loading: false, - networkStatus: 7, - stale: false, -}; - export const defaultTimelineProps: CreateTimelineProps = { from: '2018-11-05T18:58:25.937Z', timeline: { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar/index.test.tsx index 0ba6bea89e0fc..7b757f8fc621d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar/index.test.tsx @@ -18,6 +18,7 @@ import { mockHistory, Router } from '../../../../common/mock/router'; import { render, act, fireEvent } from '@testing-library/react'; import { resolveTimeline } from '../../../../timelines/containers/api'; import { mockTimeline } from '../../../../../server/lib/timeline/__mocks__/create_timelines'; +import type { ResolveTimelineResponse } from '../../../../../common/api/timeline'; jest.mock('../../../../timelines/containers/api'); jest.mock('../../../../common/lib/kibana', () => { @@ -49,6 +50,11 @@ jest.mock('../../../../timelines/containers/all', () => { }; }); +const resolvedTimeline: ResolveTimelineResponse = { + timeline: { ...mockTimeline, savedObjectId: '1', version: 'abc' }, + outcome: 'exactMatch', +}; + describe('QueryBarDefineRule', () => { beforeEach(() => { jest.clearAllMocks(); @@ -59,11 +65,7 @@ describe('QueryBarDefineRule', () => { totalCount: mockOpenTimelineQueryResults.totalCount, refetch: jest.fn(), }); - (resolveTimeline as jest.Mock).mockResolvedValue({ - data: { - timeline: { mockTimeline }, - }, - }); + (resolveTimeline as jest.Mock).mockResolvedValue(resolvedTimeline); }); it('renders correctly', () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index b16d59045d835..68c6465aabcbc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -25,7 +25,6 @@ import { getThresholdDetectionAlertAADMock, mockEcsDataWithAlert, mockTimelineDetails, - mockTimelineResult, mockAADEcsDataWithAlert, mockGetOneTimelineResult, mockTimelineData, @@ -283,7 +282,7 @@ describe('alert actions', () => { search: jest.fn().mockImplementation(() => of({ data: mockTimelineDetails })), }; - (getTimelineTemplate as jest.Mock).mockResolvedValue(mockTimelineResult); + (getTimelineTemplate as jest.Mock).mockResolvedValue(mockGetOneTimelineResult); clock = sinon.useFakeTimers(unix); }); @@ -442,11 +441,13 @@ describe('alert actions', () => { test('it invokes createTimeline with kqlQuery.filterQuery.kuery.kind as "kuery" if not specified in returned timeline template', async () => { const mockTimelineResultModified = { - ...mockTimelineResult, - kqlQuery: { - filterQuery: { - kuery: { - expression: [''], + body: { + ...mockGetOneTimelineResult, + kqlQuery: { + filterQuery: { + kuery: { + expression: [''], + }, }, }, }, @@ -460,7 +461,6 @@ describe('alert actions', () => { getExceptionFilter: mockGetExceptionFilter, }); const createTimelineArg = (createTimeline as jest.Mock).mock.calls[0][0]; - expect(mockGetExceptionFilter).not.toHaveBeenCalled(); expect(createTimeline).toHaveBeenCalledTimes(1); expect(createTimelineArg.timeline.kqlQuery.filterQuery.kuery.kind).toEqual('kuery'); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 3a3e0d0255531..822adecbe8c4d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -7,7 +7,7 @@ /* eslint-disable complexity */ -import { getOr, isEmpty } from 'lodash/fp'; +import { isEmpty } from 'lodash/fp'; import moment from 'moment'; import dateMath from '@kbn/datemath'; @@ -51,7 +51,6 @@ import { isThresholdRule, } from '../../../../common/detection_engine/utils'; import { TimelineId } from '../../../../common/types/timeline'; -import type { TimelineResponse } from '../../../../common/api/timeline'; import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline'; import type { SendAlertToTimelineActionProps, @@ -67,10 +66,7 @@ import type { } from '../../../../common/search_strategy/timeline'; import { TimelineEventsQueries } from '../../../../common/search_strategy/timeline'; import { timelineDefaults } from '../../../timelines/store/defaults'; -import { - omitTypenameInTimeline, - formatTimelineResponseToModel, -} from '../../../timelines/components/open_timeline/helpers'; +import { formatTimelineResponseToModel } from '../../../timelines/components/open_timeline/helpers'; import { convertKueryToElasticSearchQuery } from '../../../common/lib/kuery'; import { getField, getFieldKey } from '../../../helpers'; import { @@ -980,15 +976,9 @@ export const sendAlertToTimelineAction = async ({ ) ), ]); - - const resultingTimeline: TimelineResponse = getOr( - {}, - 'data.getOneTimeline', - responseTimeline - ); const eventData: TimelineEventsDetailsItem[] = eventDataResp.data ?? []; - if (!isEmpty(resultingTimeline)) { - const timelineTemplate = omitTypenameInTimeline(resultingTimeline); + if (!isEmpty(responseTimeline)) { + const timelineTemplate = responseTimeline; const { timeline, notes } = formatTimelineResponseToModel( timelineTemplate, true, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx index 6d20ef3973337..9de3d29889bbb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.test.tsx @@ -112,114 +112,110 @@ const mockSendAlertToTimeline = jest.spyOn(actions, 'sendAlertToTimelineAction') }); const mockTimelineTemplateResponse = { - data: { - getOneTimeline: { - savedObjectId: '15bc8185-06ef-4956-b7e7-be8e289b13c2', - version: 'WzIzMzUsMl0=', - columns: [ - { - columnHeaderType: 'not-filtered', - id: '@timestamp', - type: 'date', - }, - { - columnHeaderType: 'not-filtered', - id: 'host.name', - }, - { - columnHeaderType: 'not-filtered', - id: 'user.name', - }, - ], - dataProviders: [ - { - and: [], - enabled: true, - id: 'some-random-id', - name: 'host.name', - excluded: false, - kqlQuery: '', - queryMatch: { - field: 'host.name', - value: '{host.name}', - operator: ':', - }, - type: 'template', - }, - ], - dataViewId: 'security-solution-default', - description: '', - eqlOptions: { - eventCategoryField: 'event.category', - tiebreakerField: '', - timestampField: '@timestamp', - query: '', - size: 100, - }, - eventType: 'all', - excludedRowRendererIds: [ - 'alert', - 'alerts', - 'auditd', - 'auditd_file', - 'library', - 'netflow', - 'plain', - 'registry', - 'suricata', - 'system', - 'system_dns', - 'system_endgame_process', - 'system_file', - 'system_fim', - 'system_security_event', - 'system_socket', - 'threat_match', - 'zeek', - ], - favorite: [], - filters: [], - indexNames: ['.alerts-security.alerts-default', 'auditbeat-*', 'filebeat-*', 'packetbeat-*'], - kqlMode: 'filter', - kqlQuery: { - filterQuery: { - kuery: { - kind: 'kuery', - expression: '*', - }, - serializedQuery: '{"query_string":{"query":"*"}}', - }, + savedObjectId: '15bc8185-06ef-4956-b7e7-be8e289b13c2', + version: 'WzIzMzUsMl0=', + columns: [ + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + type: 'date', + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + }, + ], + dataProviders: [ + { + and: [], + enabled: true, + id: 'some-random-id', + name: 'host.name', + excluded: false, + kqlQuery: '', + queryMatch: { + field: 'host.name', + value: '{host.name}', + operator: ':', }, - title: 'Named Template', - templateTimelineId: 'c755cda6-8a65-4ec2-b6ff-35a5356de8b9', - templateTimelineVersion: 1, - dateRange: { - start: '2024-08-13T22:00:00.000Z', - end: '2024-08-14T21:59:59.999Z', + type: 'template', + }, + ], + dataViewId: 'security-solution-default', + description: '', + eqlOptions: { + eventCategoryField: 'event.category', + tiebreakerField: '', + timestampField: '@timestamp', + query: '', + size: 100, + }, + eventType: 'all', + excludedRowRendererIds: [ + 'alert', + 'alerts', + 'auditd', + 'auditd_file', + 'library', + 'netflow', + 'plain', + 'registry', + 'suricata', + 'system', + 'system_dns', + 'system_endgame_process', + 'system_file', + 'system_fim', + 'system_security_event', + 'system_socket', + 'threat_match', + 'zeek', + ], + favorite: [], + filters: [], + indexNames: ['.alerts-security.alerts-default', 'auditbeat-*', 'filebeat-*', 'packetbeat-*'], + kqlMode: 'filter', + kqlQuery: { + filterQuery: { + kuery: { + kind: 'kuery', + expression: '*', }, - savedQueryId: null, - created: 1723625359467, - createdBy: 'elastic', - updated: 1723625359988, - updatedBy: 'elastic', - timelineType: 'template', - status: 'active', - sort: [ - { - columnId: '@timestamp', - columnType: 'date', - sortDirection: 'desc', - esTypes: ['date'], - }, - ], - savedSearchId: null, - eventIdToNoteIds: [], - noteIds: [], - notes: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], + serializedQuery: '{"query_string":{"query":"*"}}', }, }, + title: 'Named Template', + templateTimelineId: 'c755cda6-8a65-4ec2-b6ff-35a5356de8b9', + templateTimelineVersion: 1, + dateRange: { + start: '2024-08-13T22:00:00.000Z', + end: '2024-08-14T21:59:59.999Z', + }, + savedQueryId: null, + created: 1723625359467, + createdBy: 'elastic', + updated: 1723625359988, + updatedBy: 'elastic', + timelineType: 'template', + status: 'active', + sort: [ + { + columnId: '@timestamp', + columnType: 'date', + sortDirection: 'desc', + esTypes: ['date'], + }, + ], + savedSearchId: null, + eventIdToNoteIds: [], + noteIds: [], + notes: [], + pinnedEventIds: [], + pinnedEventsSaveObject: [], }; const props = { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.test.ts index 57a0aa43cdde6..2a07746a6d67a 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.test.ts @@ -16,6 +16,7 @@ import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; import { mockTimeline } from '../../../../../server/lib/timeline/__mocks__/create_timelines'; import type { TimelineModel } from '../../../..'; +import type { ResolveTimelineResponse } from '../../../../../common/api/timeline'; jest.mock('../../../../common/hooks/use_experimental_features'); jest.mock('../../../../common/utils/global_query_string/helpers'); @@ -45,53 +46,52 @@ jest.mock('react-redux', () => { const timelineId = 'eb2781c0-1df5-11eb-8589-2f13958b79f7'; -const selectedTimeline = { - data: { - timeline: { - ...mockTimeline, - id: timelineId, - savedObjectId: timelineId, - indexNames: ['awesome-*'], - dataViewId: 'custom-data-view-id', - kqlQuery: { - filterQuery: { - serializedQuery: - '{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}},{"bool":{"should":[{"exists":{"field":"user.name"}}],"minimum_should_match":1}}]}}', - kuery: { - expression: 'host.name:* AND user.name:*', - kind: 'kuery', - }, +const selectedTimeline: ResolveTimelineResponse = { + outcome: 'exactMatch', + timeline: { + ...mockTimeline, + savedObjectId: timelineId, + version: 'wedwed', + indexNames: ['awesome-*'], + dataViewId: 'custom-data-view-id', + kqlQuery: { + filterQuery: { + serializedQuery: + '{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}},{"bool":{"should":[{"exists":{"field":"user.name"}}],"minimum_should_match":1}}]}}', + kuery: { + expression: 'host.name:* AND user.name:*', + kind: 'kuery', }, }, - dataProviders: [ - { - excluded: false, - and: [], - kqlQuery: '', - name: 'Stephs-MBP.lan', - queryMatch: { - field: 'host.name', - value: 'Stephs-MBP.lan', - operator: ':', - }, - id: 'draggable-badge-default-draggable-process_stopped-timeline-1-NH9UwoMB2HTqQ3G4wUFM-host_name-Stephs-MBP_lan', - enabled: true, + }, + dataProviders: [ + { + excluded: false, + and: [], + kqlQuery: '', + name: 'Stephs-MBP.lan', + queryMatch: { + field: 'host.name', + value: 'Stephs-MBP.lan', + operator: ':', }, - { - excluded: false, - and: [], - kqlQuery: '', - name: '--lang=en-US', - queryMatch: { - field: 'process.args', - value: '--lang=en-US', - operator: ':', - }, - id: 'draggable-badge-default-draggable-process_started-timeline-1-args-5---lang=en-US-MH9TwoMB2HTqQ3G4_UH--process_args---lang=en-US', - enabled: true, + id: 'draggable-badge-default-draggable-process_stopped-timeline-1-NH9UwoMB2HTqQ3G4wUFM-host_name-Stephs-MBP_lan', + enabled: true, + }, + { + excluded: false, + and: [], + kqlQuery: '', + name: '--lang=en-US', + queryMatch: { + field: 'process.args', + value: '--lang=en-US', + operator: ':', }, - ], - }, + id: 'draggable-badge-default-draggable-process_started-timeline-1-args-5---lang=en-US-MH9TwoMB2HTqQ3G4_UH--process_args---lang=en-US', + enabled: true, + }, + ], }, }; @@ -157,8 +157,8 @@ describe('useRuleFromTimeline', () => { type: 'x-pack/security_solution/local/sourcerer/SET_SELECTED_DATA_VIEW', payload: { id: 'timeline', - selectedDataViewId: selectedTimeline.data.timeline.dataViewId, - selectedPatterns: selectedTimeline.data.timeline.indexNames, + selectedDataViewId: selectedTimeline.timeline.dataViewId, + selectedPatterns: selectedTimeline.timeline.indexNames, }, }); }); @@ -220,16 +220,15 @@ describe('useRuleFromTimeline', () => { query: 'find it EQL', size: 100, }; - const eqlTimeline = { - data: { - timeline: { - ...mockTimeline, - id: timelineId, - savedObjectId: timelineId, - indexNames: ['awesome-*'], - dataViewId: 'custom-data-view-id', - eqlOptions, - }, + const eqlTimeline: ResolveTimelineResponse = { + outcome: 'exactMatch', + timeline: { + ...mockTimeline, + version: '123', + savedObjectId: timelineId, + indexNames: ['awesome-*'], + dataViewId: 'custom-data-view-id', + eqlOptions, }, }; (resolveTimeline as jest.Mock).mockResolvedValue(eqlTimeline); @@ -256,7 +255,7 @@ describe('useRuleFromTimeline', () => { const { result } = renderHook(() => useRuleFromTimeline(setRuleQuery)); expect(result.current.loading).toEqual(false); await act(async () => { - result.current.onOpenTimeline(selectedTimeline.data.timeline as unknown as TimelineModel); + result.current.onOpenTimeline(selectedTimeline.timeline as unknown as TimelineModel); }); // not loading anything as an external call to onOpenTimeline provides the timeline @@ -307,7 +306,7 @@ describe('useRuleFromTimeline', () => { const { result } = renderHook(() => useRuleFromTimeline(setRuleQuery)); expect(result.current.loading).toEqual(false); const tl = { - ...selectedTimeline.data.timeline, + ...selectedTimeline.timeline, dataProviders: [ { property: 'bad', diff --git a/x-pack/plugins/security_solution/public/notes/api/api.test.ts b/x-pack/plugins/security_solution/public/notes/api/api.test.ts index bc53b7bd78ac5..1b2250a119657 100644 --- a/x-pack/plugins/security_solution/public/notes/api/api.test.ts +++ b/x-pack/plugins/security_solution/public/notes/api/api.test.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { PersistNoteRouteResponse } from '../../../common/api/timeline'; import { KibanaServices } from '../../common/lib/kibana'; import * as api from './api'; @@ -22,22 +21,14 @@ describe('Notes API client', () => { }); describe('create note', () => { it('should throw an error when a response code other than 200 is returned', async () => { - const errorResponse: PersistNoteRouteResponse = { - data: { - persistNote: { - code: 500, - message: 'Internal server error', - note: { - timelineId: '1', - noteId: '2', - version: '3', - }, - }, - }, - }; (KibanaServices.get as jest.Mock).mockReturnValue({ http: { - patch: jest.fn().mockReturnValue(errorResponse), + patch: jest.fn().mockRejectedValue({ + body: { + status_code: 500, + message: 'Internal server error', + }, + }), }, }); diff --git a/x-pack/plugins/security_solution/public/notes/api/api.ts b/x-pack/plugins/security_solution/public/notes/api/api.ts index 892b01e3d17f0..5f380d0ae3044 100644 --- a/x-pack/plugins/security_solution/public/notes/api/api.ts +++ b/x-pack/plugins/security_solution/public/notes/api/api.ts @@ -7,7 +7,6 @@ import type { BareNote, - DeleteNoteResponse, GetNotesResponse, PersistNoteRouteResponse, } from '../../../common/api/timeline'; @@ -27,11 +26,7 @@ export const createNote = async ({ note }: { note: BareNote }) => { body: JSON.stringify({ note }), version: '2023-10-31', }); - const noteResponse = response.data.persistNote; - if (noteResponse.code !== 200) { - throw new Error(noteResponse.message); - } - return noteResponse.note; + return response.note; } catch (err) { throw new Error(('message' in err && err.message) || 'Request failed'); } @@ -98,7 +93,7 @@ export const fetchNotesBySaveObjectIds = async (savedObjectIds: string[]) => { * Deletes multiple notes */ export const deleteNotes = async (noteIds: string[]) => { - const response = await KibanaServices.get().http.delete(NOTE_URL, { + const response = await KibanaServices.get().http.delete(NOTE_URL, { body: JSON.stringify({ noteIds }), version: '2023-10-31', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts index df761c8854f41..ecd028c3d4ca9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts @@ -5,421 +5,384 @@ * 2.0. */ -import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../../common/api/timeline'; +import { + DataProviderTypeEnum, + type ResolveTimelineResponse, + TimelineStatusEnum, + TimelineTypeEnum, +} from '../../../../../common/api/timeline'; -export const mockTimeline = { - data: { - timeline: { - savedObjectId: 'eb2781c0-1df5-11eb-8589-2f13958b79f7', - columns: [ - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: '@timestamp', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'message', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'event.category', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'event.action', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'host.name', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'source.ip', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'destination.ip', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'user.name', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - ], - dataProviders: [], - dateRange: { - start: '2020-11-01T14:30:59.935Z', - end: '2020-11-03T14:31:11.417Z', - __typename: 'DateRangePickerResult', +export const mockTimeline: ResolveTimelineResponse = { + timeline: { + savedObjectId: 'eb2781c0-1df5-11eb-8589-2f13958b79f7', + columns: [ + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: '@timestamp', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'message', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'event.category', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'event.action', + name: null, + searchable: null, + type: null, }, - description: '', - eventType: 'all', - eventIdToNoteIds: [], - excludedRowRendererIds: [], - favorite: [], - filters: [], - kqlMode: 'filter', - kqlQuery: { filterQuery: null, __typename: 'SerializedFilterQueryResult' }, - indexNames: [ - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - '.siem-signals-angelachuang-default', - ], - notes: [], - noteIds: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], - status: TimelineStatusEnum.active, - title: 'my timeline', - timelineType: TimelineTypeEnum.default, - templateTimelineId: null, - templateTimelineVersion: null, - savedQueryId: null, - sort: { - columnId: '@timestamp', - columnType: 'number', - sortDirection: 'desc', - __typename: 'SortTimelineResult', + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'host.name', + name: null, + searchable: null, + type: null, }, - created: 1604497127973, - createdBy: 'elastic', - updated: 1604500278364, - updatedBy: 'elastic', - version: 'WzQ4NSwxXQ==', - __typename: 'TimelineResult', + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'source.ip', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'destination.ip', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'user.name', + name: null, + searchable: null, + type: null, + }, + ], + dataProviders: [], + dateRange: { + start: '2020-11-01T14:30:59.935Z', + end: '2020-11-03T14:31:11.417Z', }, - outcome: 'exactMatch', + description: '', + eventType: 'all', + eventIdToNoteIds: [], + excludedRowRendererIds: [], + favorite: [], + filters: [], + kqlMode: 'filter', + kqlQuery: { filterQuery: null }, + indexNames: [ + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + '.siem-signals-angelachuang-default', + ], + notes: [], + noteIds: [], + pinnedEventIds: [], + pinnedEventsSaveObject: [], + status: TimelineStatusEnum.active, + title: 'my timeline', + timelineType: TimelineTypeEnum.default, + templateTimelineId: null, + templateTimelineVersion: null, + savedQueryId: null, + sort: { + columnId: '@timestamp', + columnType: 'number', + sortDirection: 'desc', + }, + created: 1604497127973, + createdBy: 'elastic', + updated: 1604500278364, + updatedBy: 'elastic', + version: 'WzQ4NSwxXQ==', }, - loading: false, - networkStatus: 7, - stale: false, + outcome: 'exactMatch', }; -export const mockTemplate = { - data: { - timeline: { - savedObjectId: '0c70a200-1de0-11eb-885c-6fc13fca1850', - columns: [ - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: '@timestamp', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'signal.rule.description', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'event.action', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'process.name', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'process', - columnHeaderType: 'not-filtered', - description: 'The working directory of the process.', - example: '/home/alice', - indexes: null, - id: 'process.working_directory', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'process', - columnHeaderType: 'not-filtered', - description: - 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', - example: '["/usr/bin/ssh","-l","user","10.0.0.16"]', - indexes: null, - id: 'process.args', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: null, - category: null, - columnHeaderType: 'not-filtered', - description: null, - example: null, - indexes: null, - id: 'process.pid', - name: null, - searchable: null, - type: null, - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'process', - columnHeaderType: 'not-filtered', - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - indexes: null, - id: 'process.parent.executable', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'process', - columnHeaderType: 'not-filtered', - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: '["ssh","-l","user","10.0.0.16"]', - indexes: null, - id: 'process.parent.args', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'process', - columnHeaderType: 'not-filtered', - description: 'Process id.', - example: '4242', - indexes: null, - id: 'process.parent.pid', - name: null, - searchable: null, - type: 'number', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'user', - columnHeaderType: 'not-filtered', - description: 'Short name or login of the user.', - example: 'albert', - indexes: null, - id: 'user.name', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - { - aggregatable: true, - category: 'host', - columnHeaderType: 'not-filtered', - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - example: null, - indexes: null, - id: 'host.name', - name: null, - searchable: null, - type: 'string', - __typename: 'ColumnHeaderResult', - }, - ], - dataProviders: [ - { - id: 'timeline-1-8622010a-61fb-490d-b162-beac9c36a853', - name: '{process.name}', - enabled: true, - excluded: false, - kqlQuery: '', - type: 'template', - queryMatch: { - field: 'process.name', - displayField: null, - value: '{process.name}', - displayValue: null, - operator: ':', - __typename: 'QueryMatchResult', - }, - and: [], - __typename: 'DataProviderResult', - }, - { - id: 'timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568', - name: '{event.type}', - enabled: true, - excluded: false, - kqlQuery: '', - type: 'template', - queryMatch: { - field: 'event.type', - displayField: null, - value: '{event.type}', - displayValue: null, - operator: ':*', - __typename: 'QueryMatchResult', - }, - and: [], - __typename: 'DataProviderResult', +export const mockTemplate: ResolveTimelineResponse = { + timeline: { + savedObjectId: '0c70a200-1de0-11eb-885c-6fc13fca1850', + columns: [ + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: '@timestamp', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'signal.rule.description', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'event.action', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'process.name', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: true, + category: 'process', + columnHeaderType: 'not-filtered', + description: 'The working directory of the process.', + example: '/home/alice', + indexes: null, + id: 'process.working_directory', + name: null, + searchable: null, + type: 'string', + }, + { + aggregatable: true, + category: 'process', + columnHeaderType: 'not-filtered', + description: + 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', + example: '["/usr/bin/ssh","-l","user","10.0.0.16"]', + indexes: null, + id: 'process.args', + name: null, + searchable: null, + type: 'string', + }, + { + aggregatable: null, + category: null, + columnHeaderType: 'not-filtered', + description: null, + example: null, + indexes: null, + id: 'process.pid', + name: null, + searchable: null, + type: null, + }, + { + aggregatable: true, + category: 'process', + columnHeaderType: 'not-filtered', + description: 'Absolute path to the process executable.', + example: '/usr/bin/ssh', + indexes: null, + id: 'process.parent.executable', + name: null, + searchable: null, + type: 'string', + }, + { + aggregatable: true, + category: 'process', + columnHeaderType: 'not-filtered', + description: + 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', + example: '["ssh","-l","user","10.0.0.16"]', + indexes: null, + id: 'process.parent.args', + name: null, + searchable: null, + type: 'string', + }, + { + aggregatable: true, + category: 'process', + columnHeaderType: 'not-filtered', + description: 'Process id.', + example: '4242', + indexes: null, + id: 'process.parent.pid', + name: null, + searchable: null, + type: 'number', + }, + { + aggregatable: true, + category: 'user', + columnHeaderType: 'not-filtered', + description: 'Short name or login of the user.', + example: 'albert', + indexes: null, + id: 'user.name', + name: null, + searchable: null, + type: 'string', + }, + { + aggregatable: true, + category: 'host', + columnHeaderType: 'not-filtered', + description: + 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', + example: null, + indexes: null, + id: 'host.name', + name: null, + searchable: null, + type: 'string', + }, + ], + dataProviders: [ + { + id: 'timeline-1-8622010a-61fb-490d-b162-beac9c36a853', + name: '{process.name}', + enabled: true, + excluded: false, + kqlQuery: '', + type: DataProviderTypeEnum.template, + queryMatch: { + field: 'process.name', + displayField: null, + value: '{process.name}', + displayValue: null, + operator: ':', }, - ], - dateRange: { - start: '2020-10-27T14:22:11.809Z', - end: '2020-11-03T14:22:11.809Z', - __typename: 'DateRangePickerResult', + and: [], }, - description: '', - eventType: 'all', - eventIdToNoteIds: [], - excludedRowRendererIds: [], - favorite: [], - filters: [], - kqlMode: 'filter', - kqlQuery: { - filterQuery: { - kuery: { kind: 'kuery', expression: '', __typename: 'KueryFilterQueryResult' }, - serializedQuery: '', - __typename: 'SerializedKueryQueryResult', + { + id: 'timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568', + name: '{event.type}', + enabled: true, + excluded: false, + kqlQuery: '', + type: DataProviderTypeEnum.template, + queryMatch: { + field: 'event.type', + displayField: null, + value: '{event.type}', + displayValue: null, + operator: ':*', }, - __typename: 'SerializedFilterQueryResult', + and: [], }, - indexNames: [], - notes: [], - noteIds: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], - status: TimelineStatusEnum.immutable, - title: 'Generic Process Timeline', - timelineType: 'template', - templateTimelineId: 'cd55e52b-7bce-4887-88e2-f1ece4c75447', - templateTimelineVersion: 1, - savedQueryId: null, - sort: { - columnId: '@timestamp', - columnType: 'number', - sortDirection: 'desc', - __typename: 'SortTimelineResult', + ], + dateRange: { + start: '2020-10-27T14:22:11.809Z', + end: '2020-11-03T14:22:11.809Z', + }, + description: '', + eventType: 'all', + eventIdToNoteIds: [], + excludedRowRendererIds: [], + favorite: [], + filters: [], + kqlMode: 'filter', + kqlQuery: { + filterQuery: { + kuery: { kind: 'kuery', expression: '' }, + serializedQuery: '', }, - created: 1604413368243, - createdBy: 'angela', - updated: 1604413368243, - updatedBy: 'angela', - version: 'WzQwMywxXQ==', - __typename: 'TimelineResult', }, - outcome: 'exactMatch', + indexNames: [], + notes: [], + noteIds: [], + pinnedEventIds: [], + pinnedEventsSaveObject: [], + status: TimelineStatusEnum.immutable, + title: 'Generic Process Timeline', + timelineType: TimelineTypeEnum.template, + templateTimelineId: 'cd55e52b-7bce-4887-88e2-f1ece4c75447', + templateTimelineVersion: 1, + savedQueryId: null, + sort: { + columnId: '@timestamp', + columnType: 'number', + sortDirection: 'desc', + }, + created: 1604413368243, + createdBy: 'angela', + updated: 1604413368243, + updatedBy: 'angela', + version: 'WzQwMywxXQ==', }, - loading: false, - networkStatus: 7, - stale: false, + outcome: 'exactMatch', }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 525d8bba3d909..3faed3a5f7d51 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { cloneDeep, getOr, omit } from 'lodash/fp'; +import { cloneDeep, omit } from 'lodash/fp'; import { renderHook } from '@testing-library/react-hooks'; import { waitFor } from '@testing-library/react'; -import { mockTimelineResults, mockGetOneTimelineResult } from '../../../common/mock'; +import { mockTimelineResults } from '../../../common/mock'; import { timelineDefaults } from '../../store/defaults'; import type { QueryTimelineById } from './helpers'; import { @@ -17,7 +17,6 @@ import { getNotesCount, getPinnedEventCount, isUntitled, - omitTypenameInTimeline, useQueryTimelineById, formatTimelineResponseToModel, } from './helpers'; @@ -655,7 +654,7 @@ describe('helpers', () => { test('Do not override daterange if TimelineStatus is active', () => { const { timeline } = formatTimelineResponseToModel( - omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), + selectedTimeline.timeline, args.duplicate, args.timelineType ); @@ -667,7 +666,7 @@ describe('helpers', () => { describe('update a timeline', () => { const selectedTimeline = { ...mockSelectedTimeline }; - const untitledTimeline = { ...mockSelectedTimeline, title: '' }; + const untitledTimeline = { timeline: { ...mockSelectedTimeline.timeline, title: '' } }; const onOpenTimeline = jest.fn(); const args: QueryTimelineById = { duplicate: false, @@ -684,7 +683,6 @@ describe('helpers', () => { afterEach(() => { jest.clearAllMocks(); }); - test('should get timeline by Id with correct statuses', async () => { renderHook(async () => { const queryTimelineById = useQueryTimelineById(); @@ -693,7 +691,7 @@ describe('helpers', () => { // expect(resolveTimeline).toHaveBeenCalled(); const { timeline } = formatTimelineResponseToModel( - omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), + selectedTimeline.timeline, args.duplicate, args.timelineType ); @@ -751,7 +749,7 @@ describe('helpers', () => { }); test('should update timeline correctly when timeline is already saved and onOpenTimeline is not provided', async () => { - (resolveTimeline as jest.Mock).mockResolvedValue(mockSelectedTimeline); + (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline); renderHook(async () => { const queryTimelineById = useQueryTimelineById(); queryTimelineById(args); @@ -762,7 +760,7 @@ describe('helpers', () => { 1, expect.objectContaining({ timeline: expect.objectContaining({ - columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ + columns: selectedTimeline.timeline.columns!.map((col) => ({ columnHeaderType: col.columnHeaderType, id: col.id, initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) @@ -784,7 +782,7 @@ describe('helpers', () => { waitFor(() => { expect(onOpenTimeline).toHaveBeenCalledWith( expect.objectContaining({ - columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ + columns: mockSelectedTimeline.timeline.columns!.map((col) => ({ columnHeaderType: col.columnHeaderType, id: col.id, initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) @@ -827,7 +825,7 @@ describe('helpers', () => { test('override daterange if TimelineStatus is immutable', () => { const { timeline } = formatTimelineResponseToModel( - omitTypenameInTimeline(getOr({}, 'data.timeline', template)), + template.timeline, args.duplicate, args.timelineType ); @@ -841,26 +839,4 @@ describe('helpers', () => { }); }); }); - - describe('omitTypenameInTimeline', () => { - test('should not modify the passed in timeline if no __typename exists', () => { - const result = omitTypenameInTimeline(mockGetOneTimelineResult); - - expect(result).toEqual(mockGetOneTimelineResult); - }); - - test('should return timeline with __typename removed when it exists', () => { - const mockTimeline = { - ...mockGetOneTimelineResult, - __typename: 'something, something', - }; - const result = omitTypenameInTimeline(mockTimeline); - const expectedTimeline = { - ...mockTimeline, - __typename: undefined, - }; - - expect(result).toEqual(expectedTimeline); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index d3ca6c4654ff3..46ab60d9324be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -13,7 +13,6 @@ import { useDiscoverInTimelineContext } from '../../../common/components/discove import type { ColumnHeaderOptions } from '../../../../common/types/timeline'; import type { TimelineResponse, - ResolvedTimeline, ColumnHeaderResult, FilterTimelineResult, DataProviderResult, @@ -73,12 +72,6 @@ export const getNotesCount = ({ eventIdToNoteIds, noteIds }: OpenTimelineResult) export const isUntitled = ({ title }: OpenTimelineResult): boolean => title == null || title.trim().length === 0; -const omitTypename = (key: string, value: keyof TimelineModel) => - key === '__typename' ? undefined : value; - -export const omitTypenameInTimeline = (timeline: TimelineResponse): TimelineResponse => - JSON.parse(JSON.stringify(timeline), omitTypename); - const parseString = (params: string) => { try { return JSON.parse(params); @@ -348,13 +341,10 @@ export const useQueryTimelineById = () => { } else { return Promise.resolve(resolveTimeline(timelineId)) .then((result) => { - const data: ResolvedTimeline | null = getOr(null, 'data', result); - if (!data) return; - - const timelineToOpen = omitTypenameInTimeline(data.timeline); + if (!result) return; const { timeline, notes } = formatTimelineResponseToModel( - timelineToOpen, + result.timeline, duplicate, timelineType ); @@ -372,9 +362,9 @@ export const useQueryTimelineById = () => { id: TimelineId.active, notes, resolveTimelineConfig: { - outcome: data.outcome, - alias_target_id: data.alias_target_id, - alias_purpose: data.alias_purpose, + outcome: result.outcome, + alias_target_id: result.alias_target_id, + alias_purpose: result.alias_purpose, }, timeline: { ...timeline, 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 ba76f857dc34c..7640b74d1d253 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 @@ -88,17 +88,9 @@ const timelineData = { savedSearchId: null, }; const mockPatchTimelineResponse = { - data: { - persistTimeline: { - code: 200, - message: 'success', - timeline: { - ...timelineData, - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', - version: 'WzM0NSwxXQ==', - }, - }, - }, + ...timelineData, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzM0NSwxXQ==', }; describe('persistTimeline', () => { describe('create draft timeline', () => { @@ -108,15 +100,9 @@ describe('persistTimeline', () => { status: TimelineStatusEnum.draft, }; const mockDraftResponse = { - data: { - persistTimeline: { - timeline: { - ...initialDraftTimeline, - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', - version: 'WzMzMiwxXQ==', - }, - }, - }, + ...initialDraftTimeline, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzMzMiwxXQ==', }; const version = null; @@ -161,14 +147,13 @@ describe('persistTimeline', () => { test("it should update timeline from clean draft timeline's response", () => { expect(JSON.parse(patchMock.mock.calls[0][1].body)).toEqual({ - timelineId: mockDraftResponse.data.persistTimeline.timeline.savedObjectId, + timelineId: mockDraftResponse.savedObjectId, timeline: { ...initialDraftTimeline, - templateTimelineId: mockDraftResponse.data.persistTimeline.timeline.templateTimelineId, - templateTimelineVersion: - mockDraftResponse.data.persistTimeline.timeline.templateTimelineVersion, + templateTimelineId: mockDraftResponse.templateTimelineId, + templateTimelineVersion: mockDraftResponse.templateTimelineVersion, }, - version: mockDraftResponse.data.persistTimeline.timeline.version ?? '', + version: mockDraftResponse.version ?? '', }); }); }); @@ -211,13 +196,8 @@ describe('persistTimeline', () => { version, }); expect(persist).toEqual({ - data: { - persistTimeline: { - code: 403, - message: 'you do not have the permission', - timeline: { ...initialDraftTimeline, savedObjectId: '', version: '' }, - }, - }, + statusCode: 403, + message: 'you do not have the permission', }); }); }); @@ -226,15 +206,9 @@ describe('persistTimeline', () => { const timelineId = null; const importTimeline = timelineData; const mockPostTimelineResponse = { - data: { - persistTimeline: { - timeline: { - ...timelineData, - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', - version: 'WzMzMiwxXQ==', - }, - }, - }, + ...timelineData, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzMzMiwxXQ==', }; const version = null; @@ -273,17 +247,11 @@ describe('persistTimeline', () => { const timelineId = '9d5693e0-a42a-11ea-b8f4-c5434162742a'; const inputTimeline = timelineData; const mockPatchTimelineResponseNew = { - data: { - persistTimeline: { - timeline: { - ...mockPatchTimelineResponse.data.persistTimeline.timeline, - version: 'WzMzMiwxXQ==', - description: 'x', - created: 1591092702804, - updated: 1591092705206, - }, - }, - }, + ...mockPatchTimelineResponse, + version: 'WzMzMiwxXQ==', + description: 'x', + created: 1591092702804, + updated: 1591092705206, }; const version = 'initial version'; @@ -454,15 +422,9 @@ describe('cleanDraftTimeline', () => { describe('copyTimeline', () => { const mockPostTimelineResponse = { - data: { - persistTimeline: { - timeline: { - ...timelineData, - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', - version: 'WzMzMiwxXQ==', - }, - }, - }, + ...timelineData, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzMzMiwxXQ==', }; const saveSavedSearchMock = jest.fn(); 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 fa006293719d5..03f3d3ff2ff2d 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.ts @@ -67,26 +67,30 @@ const createToasterPlainError = (message: string) => new ToasterError([message]) const parseOrThrow = parseOrThrowErrorFactory(createToasterPlainError); -const decodeTimelineResponse = (respTimeline?: PersistTimelineResponse | TimelineErrorResponse) => - parseOrThrow(PersistTimelineResponse)(respTimeline); +const decodeTimelineResponse = ( + respTimeline?: PersistTimelineResponse | TimelineErrorResponse +): PersistTimelineResponse => parseOrThrow(PersistTimelineResponse)(respTimeline); -const decodeSingleTimelineResponse = (respTimeline?: GetTimelineResponse) => +const decodeSingleTimelineResponse = (respTimeline?: GetTimelineResponse): GetTimelineResponse => parseOrThrow(GetTimelineResponse)(respTimeline); -const decodeResolvedSingleTimelineResponse = (respTimeline?: ResolveTimelineResponse) => - parseOrThrow(ResolveTimelineResponse)(respTimeline); +const decodeResolvedSingleTimelineResponse = ( + respTimeline?: ResolveTimelineResponse +): ResolveTimelineResponse => parseOrThrow(ResolveTimelineResponse)(respTimeline); -const decodeGetTimelinesResponse = (respTimeline: GetTimelinesResponse) => +const decodeGetTimelinesResponse = (respTimeline: GetTimelinesResponse): GetTimelinesResponse => parseOrThrow(GetTimelinesResponse)(respTimeline); -const decodeTimelineErrorResponse = (respTimeline?: TimelineErrorResponse) => +const decodeTimelineErrorResponse = (respTimeline?: TimelineErrorResponse): TimelineErrorResponse => parseOrThrow(TimelineErrorResponse)(respTimeline); -const decodePrepackedTimelineResponse = (respTimeline?: ImportTimelineResult) => - parseOrThrow(ImportTimelineResult)(respTimeline); +const decodePrepackedTimelineResponse = ( + respTimeline?: ImportTimelineResult +): ImportTimelineResult => parseOrThrow(ImportTimelineResult)(respTimeline); -const decodeResponseFavoriteTimeline = (respTimeline?: PersistFavoriteRouteResponse) => - parseOrThrow(PersistFavoriteRouteResponse)(respTimeline); +const decodeResponseFavoriteTimeline = ( + respTimeline?: PersistFavoriteRouteResponse +): PersistFavoriteRouteResponse => parseOrThrow(PersistFavoriteRouteResponse)(respTimeline); const postTimeline = async ({ timeline, @@ -219,22 +223,19 @@ export const persistTimeline = async ({ const templateTimelineInfo = timeline.timelineType === TimelineTypeEnum.template ? { - templateTimelineId: - draftTimeline.data.persistTimeline.timeline.templateTimelineId ?? - timeline.templateTimelineId, + templateTimelineId: draftTimeline.templateTimelineId ?? timeline.templateTimelineId, templateTimelineVersion: - draftTimeline.data.persistTimeline.timeline.templateTimelineVersion ?? - timeline.templateTimelineVersion, + draftTimeline.templateTimelineVersion ?? timeline.templateTimelineVersion, } : {}; return patchTimeline({ - timelineId: draftTimeline.data.persistTimeline.timeline.savedObjectId, + timelineId: draftTimeline.savedObjectId, timeline: { ...timeline, ...templateTimelineInfo, }, - version: draftTimeline.data.persistTimeline.timeline.version ?? '', + version: draftTimeline.version ?? '', savedSearch, }); } @@ -250,19 +251,10 @@ export const persistTimeline = async ({ savedSearch, }); } catch (err) { - if (err.status_code === 403 || err.body.status_code === 403) { + if (err.status_code === 403 || err.body?.status_code === 403) { return Promise.resolve({ - data: { - persistTimeline: { - code: 403, - message: err.message || err.body.message, - timeline: { - ...timeline, - savedObjectId: '', - version: '', - }, - }, - }, + statusCode: 403, + message: err.message || err.body.message, }); } return Promise.resolve(err); diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.test.ts index 85bf3652ab0b9..8ae986e95c4a9 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.test.ts @@ -39,16 +39,8 @@ describe('Timeline middleware helpers', () => { it('should return a draft timeline with a savedObjectId when an unsaved timeline is passed', async () => { const mockSavedObjectId = 'mockSavedObjectId'; (persistTimeline as jest.Mock).mockResolvedValue({ - data: { - persistTimeline: { - code: 200, - message: 'success', - timeline: { - ...mockGlobalState.timeline.timelineById[TimelineId.test], - savedObjectId: mockSavedObjectId, - }, - }, - }, + ...mockGlobalState.timeline.timelineById[TimelineId.test], + savedObjectId: mockSavedObjectId, }); const returnedTimeline = await ensureTimelineIsSaved({ diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.ts index c5cb62b523bb8..adeca92795178 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/helpers.ts @@ -6,6 +6,7 @@ */ import type { MiddlewareAPI, Dispatch, AnyAction } from 'redux'; +import type { IHttpFetchError } from '@kbn/core/public'; import type { State } from '../../../common/store/types'; import { ALL_TIMELINE_QUERY_ID } from '../../containers/all'; import type { inputsModel } from '../../../common/store/inputs'; @@ -62,3 +63,17 @@ export async function ensureTimelineIsSaved({ // Make sure we're returning the most updated version of the timeline return selectTimelineById(store.getState(), localTimelineId); } + +export function isHttpFetchError( + error: unknown +): error is IHttpFetchError<{ status_code: number }> { + return ( + error !== null && + typeof error === 'object' && + 'body' in error && + error.body !== null && + typeof error.body === 'object' && + `status_code` in error.body && + typeof error.body.status_code === 'number' + ); +} diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.test.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.test.ts index 50a0ed53c4913..01be4f72c83f4 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.test.ts @@ -35,7 +35,14 @@ jest.mock('../actions', () => { }; }); jest.mock('../../containers/api'); -jest.mock('./helpers'); +jest.mock('./helpers', () => { + const actual = jest.requireActual('./helpers'); + + return { + ...actual, + refreshTimelines: jest.fn(), + }; +}); const startTimelineSavingMock = startTimelineSaving as unknown as jest.Mock; const endTimelineSavingMock = endTimelineSaving as unknown as jest.Mock; @@ -53,14 +60,9 @@ describe('Timeline favorite middleware', () => { it('should persist a timeline favorite when a favorite action is dispatched', async () => { (persistFavorite as jest.Mock).mockResolvedValue({ - data: { - persistFavorite: { - code: 200, - favorite: [{}], - savedObjectId: newSavedObjectId, - version: newVersion, - }, - }, + favorite: [{}], + savedObjectId: newSavedObjectId, + version: newVersion, }); expect(selectTimelineById(store.getState(), TimelineId.test).isFavorite).toEqual(false); await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: true })); @@ -88,14 +90,9 @@ describe('Timeline favorite middleware', () => { }) ); (persistFavorite as jest.Mock).mockResolvedValue({ - data: { - persistFavorite: { - code: 200, - favorite: [], - savedObjectId: newSavedObjectId, - version: newVersion, - }, - }, + favorite: [], + savedObjectId: newSavedObjectId, + version: newVersion, }); expect(selectTimelineById(store.getState(), TimelineId.test).isFavorite).toEqual(true); await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: false })); @@ -113,12 +110,8 @@ describe('Timeline favorite middleware', () => { }); it('should show an error message when the call is unauthorized', async () => { - (persistFavorite as jest.Mock).mockResolvedValue({ - data: { - persistFavorite: { - code: 403, - }, - }, + (persistFavorite as jest.Mock).mockRejectedValue({ + body: { status_code: 403 }, }); await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: true })); diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.ts index bf4854e60666b..4f195e5fa13f6 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_favorite.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { get } from 'lodash/fp'; import type { Action, Middleware } from 'redux'; import type { CoreStart } from '@kbn/core/public'; @@ -17,12 +16,11 @@ import { startTimelineSaving, showCallOutUnauthorizedMsg, } from '../actions'; -import type { FavoriteTimelineResponse } from '../../../../common/api/timeline'; import { TimelineTypeEnum } from '../../../../common/api/timeline'; import { persistFavorite } from '../../containers/api'; import { selectTimelineById } from '../selectors'; import * as i18n from '../../pages/translations'; -import { refreshTimelines } from './helpers'; +import { isHttpFetchError, refreshTimelines } from './helpers'; type FavoriteTimelineAction = ReturnType; @@ -42,19 +40,13 @@ export const favoriteTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, S store.dispatch(startTimelineSaving({ id })); try { - const result = await persistFavorite({ + const response = await persistFavorite({ timelineId: timeline.id, templateTimelineId: timeline.templateTimelineId, templateTimelineVersion: timeline.templateTimelineVersion, timelineType: timeline.timelineType ?? TimelineTypeEnum.default, }); - const response: FavoriteTimelineResponse = get('data.persistFavorite', result); - - if (response.code === 403) { - store.dispatch(showCallOutUnauthorizedMsg()); - } - refreshTimelines(store.getState()); store.dispatch( @@ -69,10 +61,14 @@ export const favoriteTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, S }) ); } catch (error) { - kibana.notifications.toasts.addDanger({ - title: i18n.UPDATE_TIMELINE_ERROR_TITLE, - text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, - }); + if (isHttpFetchError(error) && error.body?.status_code === 403) { + store.dispatch(showCallOutUnauthorizedMsg()); + } else { + kibana.notifications.toasts.addDanger({ + title: i18n.UPDATE_TIMELINE_ERROR_TITLE, + text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, + }); + } } finally { store.dispatch( endTimelineSaving({ diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.test.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.test.ts index 9fb0585601042..f6f3a0d0dc151 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.test.ts @@ -70,14 +70,8 @@ describe('Timeline note middleware', () => { it('should persist a timeline note', async () => { (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 200, - message: 'success', - note: { - noteId: testNote.id, - }, - }, + note: { + noteId: testNote.id, }, }); expect(selectTimelineById(store.getState(), TimelineId.test).noteIds).toEqual([]); @@ -92,14 +86,8 @@ describe('Timeline note middleware', () => { it('should persist a note on an event of a timeline', async () => { (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 200, - message: 'success', - note: { - noteId: testNote.id, - }, - }, + note: { + noteId: testNote.id, }, }); expect(selectTimelineById(store.getState(), TimelineId.test).eventIdToNoteIds).toEqual({ @@ -123,14 +111,8 @@ describe('Timeline note middleware', () => { it('should ensure the timeline is saved or in draft mode before creating a note', async () => { (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 200, - message: 'success', - note: { - noteId: testNote.id, - }, - }, + note: { + noteId: testNote.id, }, }); @@ -159,15 +141,9 @@ describe('Timeline note middleware', () => { it('should pin the event when the event is not pinned yet', async () => { const testTimelineId = 'testTimelineId'; (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 200, - message: 'success', - note: { - noteId: testNote.id, - timelineId: testTimelineId, - }, - }, + note: { + noteId: testNote.id, + timelineId: testTimelineId, }, }); @@ -207,15 +183,9 @@ describe('Timeline note middleware', () => { ); const testTimelineId = 'testTimelineId'; (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 200, - message: 'success', - note: { - noteId: testNote.id, - timelineId: testTimelineId, - }, - }, + note: { + noteId: testNote.id, + timelineId: testTimelineId, }, }); @@ -232,12 +202,8 @@ describe('Timeline note middleware', () => { }); it('should show an error message when the call is unauthorized', async () => { - (persistNote as jest.Mock).mockResolvedValue({ - data: { - persistNote: { - code: 403, - }, - }, + (persistNote as jest.Mock).mockRejectedValue({ + body: { status_code: 403 }, }); await store.dispatch(updateNote({ note: testNote })); diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.ts index 876fd613d0791..44b6e6d872037 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_note.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { get } from 'lodash/fp'; import type { Action, Middleware } from 'redux'; import type { CoreStart } from '@kbn/core/public'; @@ -22,10 +21,9 @@ import { pinEvent, } from '../actions'; import { persistNote } from '../../containers/notes/api'; -import type { ResponseNote } from '../../../../common/api/timeline'; import { selectTimelineById } from '../selectors'; import * as i18n from '../../pages/translations'; -import { ensureTimelineIsSaved, refreshTimelines } from './helpers'; +import { ensureTimelineIsSaved, isHttpFetchError, refreshTimelines } from './helpers'; type NoteAction = ReturnType; @@ -64,7 +62,7 @@ export const addNoteToTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, throw new Error('Cannot create note without a timelineId'); } - const result = await persistNote({ + const response = await persistNote({ noteId: null, version: null, note: { @@ -74,11 +72,6 @@ export const addNoteToTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, }, }); - const response: ResponseNote = get('data.persistNote', result); - if (response.code === 403) { - store.dispatch(showCallOutUnauthorizedMsg()); - } - refreshTimelines(store.getState()); await store.dispatch( @@ -112,10 +105,14 @@ export const addNoteToTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, } } } catch (error) { - kibana.notifications.toasts.addDanger({ - title: i18n.UPDATE_TIMELINE_ERROR_TITLE, - text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, - }); + if (isHttpFetchError(error) && error.body?.status_code === 403) { + store.dispatch(showCallOutUnauthorizedMsg()); + } else { + kibana.notifications.toasts.addDanger({ + title: i18n.UPDATE_TIMELINE_ERROR_TITLE, + text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, + }); + } } finally { store.dispatch( endTimelineSaving({ diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.test.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.test.ts index 67374a66019db..7108ecfc4a616 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.test.ts @@ -63,12 +63,7 @@ describe('Timeline pinned event middleware', () => { it('should persist a timeline pin event action', async () => { (persistPinnedEvent as jest.Mock).mockResolvedValue({ - data: { - persistPinnedEventOnTimeline: { - code: 200, - eventId: testEventId, - }, - }, + eventId: testEventId, }); expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({}); await store.dispatch(pinEvent({ id: TimelineId.test, eventId: testEventId })); @@ -103,7 +98,7 @@ describe('Timeline pinned event middleware', () => { ); (persistPinnedEvent as jest.Mock).mockResolvedValue({ - data: {}, + unpinned: true, }); expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({ [testEventId]: true, @@ -117,13 +112,7 @@ describe('Timeline pinned event middleware', () => { }); it('should ensure the timeline is saved or in draft mode before pinning an event', async () => { - (persistPinnedEvent as jest.Mock).mockResolvedValue({ - data: { - persistPinnedEventOnTimeline: { - code: 200, - }, - }, - }); + (persistPinnedEvent as jest.Mock).mockResolvedValue({}); expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({}); await store.dispatch(pinEvent({ id: TimelineId.test, eventId: testEventId })); @@ -139,12 +128,8 @@ describe('Timeline pinned event middleware', () => { }); it('should show an error message when the call is unauthorized', async () => { - (persistPinnedEvent as jest.Mock).mockResolvedValue({ - data: { - persistPinnedEventOnTimeline: { - code: 403, - }, - }, + (persistPinnedEvent as jest.Mock).mockRejectedValue({ + body: { status_code: 403 }, }); await store.dispatch(unPinEvent({ id: TimelineId.test, eventId: testEventId })); diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.ts index 38e00af3f5f8e..8ef7a661cc6eb 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_pinned_event.ts @@ -21,7 +21,7 @@ import { showCallOutUnauthorizedMsg, } from '../actions'; import { persistPinnedEvent } from '../../containers/pinned_event/api'; -import { ensureTimelineIsSaved, refreshTimelines } from './helpers'; +import { ensureTimelineIsSaved, isHttpFetchError, refreshTimelines } from './helpers'; type PinnedEventAction = ReturnType; @@ -55,7 +55,7 @@ export const addPinnedEventToTimelineMiddleware: (kibana: CoreStart) => Middlewa throw new Error('Cannot create a pinned event without a timelineId'); } - const result = await persistPinnedEvent({ + const response = await persistPinnedEvent({ pinnedEventId: timeline.pinnedEventsSaveObject[eventId] != null ? timeline.pinnedEventsSaveObject[eventId].pinnedEventId @@ -64,17 +64,10 @@ export const addPinnedEventToTimelineMiddleware: (kibana: CoreStart) => Middlewa timelineId: timeline.savedObjectId, }); - const response = result.data.persistPinnedEventOnTimeline; - if (response && 'code' in response && response.code === 403) { - store.dispatch(showCallOutUnauthorizedMsg()); - } - refreshTimelines(store.getState()); const currentTimeline = selectTimelineById(store.getState(), action.payload.id); - // The response is null or empty in case we unpinned an event. - // In that case we want to remove the locally pinned event. - if (!response || !('eventId' in response)) { + if ('unpinned' in response) { return store.dispatch( updateTimeline({ id: action.payload.id, @@ -106,10 +99,14 @@ export const addPinnedEventToTimelineMiddleware: (kibana: CoreStart) => Middlewa ); } } catch (error) { - kibana.notifications.toasts.addDanger({ - title: i18n.UPDATE_TIMELINE_ERROR_TITLE, - text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, - }); + if (isHttpFetchError(error) && error.body?.status_code === 403) { + store.dispatch(showCallOutUnauthorizedMsg()); + } else { + kibana.notifications.toasts.addDanger({ + title: i18n.UPDATE_TIMELINE_ERROR_TITLE, + text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, + }); + } } finally { store.dispatch( endTimelineSaving({ diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.test.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.test.ts index c3d7a26d7b027..29c2ad49ba3a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.test.ts @@ -58,16 +58,8 @@ describe('Timeline save middleware', () => { it('should persist a timeline', async () => { (persistTimeline as jest.Mock).mockResolvedValue({ - data: { - persistTimeline: { - code: 200, - message: 'success', - timeline: { - savedObjectId: 'soid', - version: 'newVersion', - }, - }, - }, + savedObjectId: 'soid', + version: 'newVersion', }); await store.dispatch(setChanged({ id: TimelineId.test, changed: true })); expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual( @@ -92,16 +84,8 @@ describe('Timeline save middleware', () => { it('should copy a timeline', async () => { (copyTimeline as jest.Mock).mockResolvedValue({ - data: { - persistTimeline: { - code: 200, - message: 'success', - timeline: { - savedObjectId: 'soid', - version: 'newVersion', - }, - }, - }, + savedObjectId: 'soid', + version: 'newVersion', }); await store.dispatch(setChanged({ id: TimelineId.test, changed: true })); expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual( diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.ts index a0d0ab4dd1061..08cbb6bfa3ea0 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/timeline_save.ts @@ -63,7 +63,7 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State store.dispatch(startTimelineSaving({ id: localTimelineId })); try { - const result = await (action.payload.saveAsNew && timeline.id + const response = await (action.payload.saveAsNew && timeline.id ? copyTimeline({ timelineId, timeline: { @@ -84,8 +84,8 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State savedSearch: timeline.savedSearch, })); - if (isTimelineErrorResponse(result)) { - const error = getErrorFromResponse(result); + if (isTimelineErrorResponse(response)) { + const error = getErrorFromResponse(response); switch (error?.errorCode) { case 403: store.dispatch(showCallOutUnauthorizedMsg()); @@ -106,7 +106,6 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State return; } - const response = result?.data?.persistTimeline; if (response == null) { kibana.notifications.toasts.addDanger({ title: i18n.UPDATE_TIMELINE_ERROR_TITLE, @@ -122,15 +121,15 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State id: localTimelineId, timeline: { ...timeline, - id: response.timeline.savedObjectId, - updated: response.timeline.updated ?? undefined, - savedObjectId: response.timeline.savedObjectId, - version: response.timeline.version, - status: response.timeline.status ?? TimelineStatusEnum.active, - timelineType: response.timeline.timelineType ?? TimelineTypeEnum.default, - templateTimelineId: response.timeline.templateTimelineId ?? null, - templateTimelineVersion: response.timeline.templateTimelineVersion ?? null, - savedSearchId: response.timeline.savedSearchId ?? null, + id: response.savedObjectId, + updated: response.updated ?? undefined, + savedObjectId: response.savedObjectId, + version: response.version, + status: response.status ?? TimelineStatusEnum.active, + timelineType: response.timelineType ?? TimelineTypeEnum.default, + templateTimelineId: response.templateTimelineId ?? null, + templateTimelineVersion: response.templateTimelineVersion ?? null, + savedSearchId: response.savedSearchId ?? null, isSaving: false, }, }) diff --git a/x-pack/plugins/security_solution/server/lib/timeline/__mocks__/create_timelines.ts b/x-pack/plugins/security_solution/server/lib/timeline/__mocks__/create_timelines.ts index aee26dc06bc77..fcccd38191a7d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/__mocks__/create_timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/__mocks__/create_timelines.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline'; + export const mockTemplate = { columns: [ { @@ -172,13 +174,13 @@ export const mockTemplate = { kqlQuery: { filterQuery: { kuery: { kind: 'kuery', expression: '' }, serializedQuery: '' } }, indexNames: [], title: 'Generic Process Timeline - Duplicate - Duplicate', - timelineType: 'template', + timelineType: TimelineTypeEnum.template, templateTimelineVersion: null, templateTimelineId: null, dateRange: { start: '2020-10-01T11:37:31.655Z', end: '2020-10-02T11:37:31.655Z' }, savedQueryId: null, sort: { columnId: '@timestamp', sortDirection: 'desc' }, - status: 'active', + status: TimelineStatusEnum.active, }; export const mockTimeline = { @@ -210,11 +212,11 @@ export const mockTimeline = { '.siem-signals-angelachuang-default', ], title: 'my timeline', - timelineType: 'default', + timelineType: TimelineTypeEnum.default, templateTimelineVersion: null, templateTimelineId: null, dateRange: { start: '2020-11-03T13:34:40.339Z', end: '2020-11-04T13:34:40.339Z' }, savedQueryId: null, sort: { columnId: '@timestamp', columnType: 'number', sortDirection: 'desc' }, - status: 'draft', + status: TimelineStatusEnum.draft, }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/README.md b/x-pack/plugins/security_solution/server/lib/timeline/routes/README.md deleted file mode 100644 index 6878e21e14452..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/README.md +++ /dev/null @@ -1,1439 +0,0 @@ -**Timeline apis** - - 1. Create timeline api - 2. Update timeline api - 3. Create template timeline api - 4. Update template timeline api - - -## Create timeline api -#### POST /api/timeline -##### Authorization -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` -##### Request body -```json -{ - "timeline": { - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [], - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": null - }, - "title": "abd", - "dateRange": { - "start": 1587370079200, - "end": 1587456479201 - }, - "savedQueryId": null, - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - } - }, - "timelineId":null, // Leave this as null - "version":null // Leave this as null -} -``` - - -## Update timeline api -#### PATCH /api/timeline -##### Authorization -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` -##### Request body -```json -{ - "timeline": { - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [], - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": null - }, - "title": "abd", - "dateRange": { - "start": 1587370079200, - "end": 1587456479201 - }, - "savedQueryId": null, - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - }, - "created": 1587468588922, - "createdBy": "casetester", - "updated": 1587468588922, - "updatedBy": "casetester", - "timelineType": "default" - }, - "timelineId":"68ea5330-83c3-11ea-bff9-ab01dd7cb6cc", // Have to match the existing timeline savedObject id - "version":"WzYwLDFd" // Have to match the existing timeline version -} -``` - -## Create template timeline api -#### POST /api/timeline -##### Authorization -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` -##### Request body -```json -{ - "timeline": { - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [ - - ], - "description": "", - "eventType": "all", - "filters": [ - - ], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": null - }, - "title": "abd", - "dateRange": { - "start": 1587370079200, - "end": 1587456479201 - }, - "savedQueryId": null, - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - }, - "timelineType": "template" // This is the difference between create timeline - }, - "timelineId":null, // Leave this as null - "version":null // Leave this as null -} -``` - - -## Update template timeline api -#### PATCH /api/timeline -##### Authorization -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` -##### Request body -```json -{ - "timeline": { - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [], - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": null - }, - "title": "abd", - "dateRange": { - "start": 1587370079200, - "end": 1587456479201 - }, - "savedQueryId": null, - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - }, - "timelineType": "template", - "created": 1587473119992, - "createdBy": "casetester", - "updated": 1587473119992, - "updatedBy": "casetester", - "templateTimelineId": "745d0316-6af7-43bf-afd6-9747119754fb", // Please provide the existing template timeline version - "templateTimelineVersion": 2 // Please provide a template timeline version grater than existing one - }, - "timelineId":"f5a4bd10-83cd-11ea-bf78-0547a65f1281", // This is a must as well - "version":"Wzg2LDFd" // Please provide the existing timeline version -} -``` - -## Export timeline api - -#### POST /api/timeline/_export - -##### Authorization - -Type: Basic Auth - -username: Your Kibana username - -password: Your Kibana password - - - - -##### Request header - -``` - -Content-Type: application/json - -kbn-version: 8.0.0 - -``` - -##### Request param - -``` -file_name: ${filename}.ndjson -``` - -##### Request body -```json -{ - ids: [ - ${timelineId} - ] -} -``` - -## Import timeline api - -#### POST /api/timeline/_import - -##### Authorization - -Type: Basic Auth - -username: Your Kibana username - -password: Your Kibana password - - - - -##### Request header - -``` - -Content-Type: application/json - -kbn-version: 8.0.0 - -``` - -##### Request body - -``` -{ - file: sample.ndjson -} -``` - - -(each json in the file should match this format) -example: -``` -{"savedObjectId":"a3002fd0-781b-11ea-85e4-df9002f1452c","version":"WzIzLDFd","columns":[{"columnHeaderType":"not-filtered","id":"@timestamp"},{"columnHeaderType":"not-filtered","id":"message"},{"columnHeaderType":"not-filtered","id":"event.category"},{"columnHeaderType":"not-filtered","id":"event.action"},{"columnHeaderType":"not-filtered","id":"host.name"},{"columnHeaderType":"not-filtered","id":"source.ip"},{"columnHeaderType":"not-filtered","id":"destination.ip"},{"columnHeaderType":"not-filtered","id":"user.name"}],"dataProviders":[],"description":"tes description","eventType":"all","filters":[{"meta":{"field":null,"negate":false,"alias":null,"disabled":false,"params":"{\"query\":\"MacBook-Pro-de-Gloria.local\"}","type":"phrase","key":"host.name"},"query":"{\"match_phrase\":{\"host.name\":\"MacBook-Pro-de-Gloria.local\"}}","missing":null,"exists":null,"match_all":null,"range":null,"script":null}],"kqlMode":"filter","kqlQuery":{"filterQuery":{"serializedQuery":"{\"bool\":{\"should\":[{\"exists\":{\"field\":\"host.name\"}}],\"minimum_should_match\":1}}","kuery":{"expression":"host.name: *","kind":"kuery"}}},"title":"Test","dateRange":{"start":1585227005527,"end":1585313405527},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1586187068132,"createdBy":"angela","updated":1586187068132,"updatedBy":"angela","eventNotes":[],"globalNotes":[{"noteId":"a3b4d9d0-781b-11ea-85e4-df9002f1452c","version":"WzI1LDFd","note":"this is a note","timelineId":"a3002fd0-781b-11ea-85e4-df9002f1452c","created":1586187069313,"createdBy":"angela","updated":1586187069313,"updatedBy":"angela"}],"pinnedEventIds":[]} -``` - -##### Response -``` -{"success":true,"success_count":1,"errors":[]} -``` - -## Get draft timeline api - -#### GET /api/timeline/_draft - -##### Authorization - -Type: Basic Auth - -username: Your Kibana username - -password: Your Kibana password - - -##### Request header - -``` - -Content-Type: application/json - -kbn-version: 8.0.0 - -``` - -##### Request param -``` -timelineType: `default` or `template` -``` - -##### Response -```json -{ - "data": { - "persistTimeline": { - "timeline": { - "savedObjectId": "ababbd90-99de-11ea-8446-1d7fd9f03ebf", - "version": "WzM2MiwzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [], - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "timelineType": "default", - "kqlQuery": { - "filterQuery": null - }, - "title": "", - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - }, - "status": "draft", - "created": 1589899222908, - "createdBy": "casetester", - "updated": 1589899222908, - "updatedBy": "casetester", - "templateTimelineId": null, - "templateTimelineVersion": null, - "favorite": [], - "eventIdToNoteIds": [], - "noteIds": [], - "notes": [], - "pinnedEventIds": [], - "pinnedEventsSaveObject": [] - } - } - } -} -``` - -## Create draft timeline api - -#### POST /api/timeline/_draft - -##### Authorization - -Type: Basic Auth - -username: Your Kibana username - -password: Your Kibana password - - -##### Request header - -``` - -Content-Type: application/json - -kbn-version: 8.0.0 - -``` - -##### Request body - -```json -{ - "timelineType": "default" or "template" -} -``` - -##### Response -```json -{ - "data": { - "persistTimeline": { - "timeline": { - "savedObjectId": "ababbd90-99de-11ea-8446-1d7fd9f03ebf", - "version": "WzQyMywzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [], - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "timelineType": "default", - "kqlQuery": { - "filterQuery": null - }, - "title": "", - "sort": { - "columnId": "@timestamp", - "sortDirection": "desc" - }, - "status": "draft", - "created": 1589903306582, - "createdBy": "casetester", - "updated": 1589903306582, - "updatedBy": "casetester", - "templateTimelineId": null, - "templateTimelineVersion": null, - "favorite": [], - "eventIdToNoteIds": [], - "noteIds": [], - "notes": [], - "pinnedEventIds": [], - "pinnedEventsSaveObject": [] - } - } - } -} -``` - -## Get timelines / timeline templates api - -#### GET /api/timelines - - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` - -##### Query params - -optional: -only_user_favorite={boolean} -page_index={number} -page_size={number} -search={string} -sort_field={title|description|updated|created} -sort_order={asc|desc} -status={active|draft|immutable} -timeline_type={default|template} - -##### example -api/timelines?page_size=10&page_index=1&sort_field=updated&sort_order=desc&timeline_type=default - -##### Response - -```json -{ - "totalCount": 2, - "timeline": [ - { - "savedObjectId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4NywzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp", - "type": "number" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [ - { - "excluded": false, - "and": [], - "kqlQuery": "", - "name": "", - "queryMatch": { - "field": "host.name", - "value": "", - "operator": ":*" - }, - "id": "timeline-1-db9f4fc8-9420-420e-8e67-b12dd36691f6", - "type": "default", - "enabled": true - } - ], - "dataViewId": "security-solution", - "description": "", - "eqlOptions": { - "tiebreakerField": "", - "size": 100, - "query": "", - "eventCategoryField": "event.category", - "timestampField": "@timestamp" - }, - "eventType": "all", - "excludedRowRendererIds": [], - "filters": [], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": null - }, - "indexNames": [ - ".siem-signals-angelachuang-default", - "auditbeat-*", - "endgame-*", - "filebeat-*", - "logs-*", - "packetbeat-*", - "winlogbeat-*" - ], - "title": "timeline - Duplicate", - "timelineType": "default", - "templateTimelineVersion": null, - "templateTimelineId": null, - "dateRange": { - "start": "2021-03-25T05:38:55.593Z", - "end": "2021-03-26T15:59:59.999Z" - }, - "savedQueryId": null, - "sort": [ - { - "columnType": "number", - "sortDirection": "desc", - "columnId": "@timestamp" - } - ], - "status": "active", - "created": 1616757027458, - "createdBy": "angela", - "updated": 1616758738320, - "updatedBy": "angela", - "favorite": [], - "eventIdToNoteIds": [ - { - "noteId": "e6f3a9a0-8e23-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4MywzXQ==", - "eventId": "QN84bngBYJMSg9tnAi1V", - "note": "note!", - "timelineId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "created": 1616757041466, - "createdBy": "angela", - "updated": 1616757041466, - "updatedBy": "angela" - } - ], - "noteIds": [ - "221524f0-8e24-11eb-ad8a-a192243e45e8" - ], - "notes": [ - { - "noteId": "e6f3a9a0-8e23-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4MywzXQ==", - "eventId": "QN84bngBYJMSg9tnAi1V", - "note": "note!", - "timelineId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "created": 1616757041466, - "createdBy": "angela", - "updated": 1616757041466, - "updatedBy": "angela" - }, - { - "noteId": "221524f0-8e24-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4NiwzXQ==", - "note": "global note!", - "timelineId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "created": 1616757140671, - "createdBy": "angela", - "updated": 1616757140671, - "updatedBy": "angela" - } - ], - "pinnedEventIds": [ - "QN84bngBYJMSg9tnAi1V", - "P984bngBYJMSg9tnAi1V" - ], - "pinnedEventsSaveObject": [ - { - "pinnedEventId": "e85339a0-8e23-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4NCwzXQ==", - "eventId": "QN84bngBYJMSg9tnAi1V", - "timelineId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "created": 1616757043770, - "createdBy": "angela", - "updated": 1616757043770, - "updatedBy": "angela" - }, - { - "pinnedEventId": "2945cfe0-8e24-11eb-ad8a-a192243e45e8", - "version": "WzM1NzQ4NSwzXQ==", - "eventId": "P984bngBYJMSg9tnAi1V", - "timelineId": "de9a3620-8e23-11eb-ad8a-a192243e45e8", - "created": 1616757152734, - "createdBy": "angela", - "updated": 1616757152734, - "updatedBy": "angela" - } - ] - }, - { - "savedObjectId": "48870270-8e1f-11eb-9cbd-7f6324a02fb7", - "version": "WzM1NzQ4MiwzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp", - "type": "number" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [ - { - "excluded": false, - "and": [], - "kqlQuery": "", - "name": "", - "queryMatch": { - "field": "host.name", - "value": "", - "operator": ":*" - }, - "id": "timeline-1-db9f4fc8-9420-420e-8e67-b12dd36691f6", - "type": "default", - "enabled": true - } - ], - "dataViewId": "security-solution", - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "timelineType": "default", - "kqlQuery": { - "filterQuery": null - }, - "title": "timeline", - "sort": [ - { - "columnType": "number", - "sortDirection": "desc", - "columnId": "@timestamp" - } - ], - "status": "active", - "created": 1616755057686, - "createdBy": "angela", - "updated": 1616756755376, - "updatedBy": "angela", - "templateTimelineId": null, - "templateTimelineVersion": null, - "excludedRowRendererIds": [], - "dateRange": { - "start": "2021-03-25T16:00:00.000Z", - "end": "2021-03-26T15:59:59.999Z" - }, - "indexNames": [ - "auditbeat-*", - "endgame-*", - "filebeat-*", - "logs-*", - "packetbeat-*", - "winlogbeat-*", - ".siem-signals-angelachuang-default" - ], - "eqlOptions": { - "tiebreakerField": "", - "size": 100, - "query": "", - "eventCategoryField": "event.category", - "timestampField": "@timestamp" - }, - "savedQueryId": null, - "favorite": [ - { - "favoriteDate": 1616756755376, - "keySearch": "YW5nZWxh", - "fullName": "Angela", - "userName": "angela" - } - ], - "eventIdToNoteIds": [], - "noteIds": [], - "notes": [], - "pinnedEventIds": [], - "pinnedEventsSaveObject": [] - } - ], - "defaultTimelineCount": 2, - "templateTimelineCount": 4, - "elasticTemplateTimelineCount": 3, - "customTemplateTimelineCount": 1, - "favoriteCount": 1 -} -``` - -## Get timeline api - -#### GET /api/id?id={savedObjectId} - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` - -##### Response -```json -{ - "data": { - "getOneTimeline": { - "savedObjectId": "48870270-8e1f-11eb-9cbd-7f6324a02fb7", - "version": "WzM1NzQ4MiwzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp", - "type": "number" - }, - { - "columnHeaderType": "not-filtered", - "id": "message" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.category" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "host.name" - }, - { - "columnHeaderType": "not-filtered", - "id": "source.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "destination.ip" - }, - { - "columnHeaderType": "not-filtered", - "id": "user.name" - } - ], - "dataProviders": [ - { - "excluded": false, - "and": [], - "kqlQuery": "", - "name": "", - "queryMatch": { - "field": "host.name", - "value": "", - "operator": ":*" - }, - "id": "timeline-1-db9f4fc8-9420-420e-8e67-b12dd36691f6", - "type": "default", - "enabled": true - } - ], - "dataViewId": "security-solution", - "description": "", - "eventType": "all", - "filters": [], - "kqlMode": "filter", - "timelineType": "default", - "kqlQuery": { - "filterQuery": null - }, - "title": "timeline", - "sort": [ - { - "columnType": "number", - "sortDirection": "desc", - "columnId": "@timestamp" - } - ], - "status": "active", - "created": 1616755057686, - "createdBy": "angela", - "updated": 1616756755376, - "updatedBy": "angela", - "templateTimelineId": null, - "templateTimelineVersion": null, - "excludedRowRendererIds": [], - "dateRange": { - "start": "2021-03-25T16:00:00.000Z", - "end": "2021-03-26T15:59:59.999Z" - }, - "indexNames": [ - "auditbeat-*", - "endgame-*", - "filebeat-*", - "logs-*", - "packetbeat-*", - "winlogbeat-*", - ".siem-signals-angelachuang-default" - ], - "eqlOptions": { - "tiebreakerField": "", - "size": 100, - "query": "", - "eventCategoryField": "event.category", - "timestampField": "@timestamp" - }, - "savedQueryId": null, - "favorite": [ - { - "favoriteDate": 1616756755376, - "keySearch": "YW5nZWxh", - "fullName": "Angela", - "userName": "angela" - } - ], - "eventIdToNoteIds": [], - "noteIds": [], - "notes": [], - "pinnedEventIds": [], - "pinnedEventsSaveObject": [] - } - } -} -``` - - -## Get timeline template api - -#### GET /api/timeline?template_timeline_id={templateTimelineId} - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` - -##### Response -```json -{ - "data": { - "getOneTimeline": { - "savedObjectId": "bf662160-9788-11eb-8277-3516cc4109c3", - "version": "WzM1NzU2MCwzXQ==", - "columns": [ - { - "columnHeaderType": "not-filtered", - "id": "@timestamp" - }, - { - "columnHeaderType": "not-filtered", - "id": "signal.rule.description" - }, - { - "columnHeaderType": "not-filtered", - "id": "event.action" - }, - { - "columnHeaderType": "not-filtered", - "id": "process.name" - }, - { - "aggregatable": true, - "description": "The working directory of the process.", - "columnHeaderType": "not-filtered", - "id": "process.working_directory", - "category": "process", - "type": "string", - "example": "/home/alice" - }, - { - "aggregatable": true, - "description": "Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.", - "columnHeaderType": "not-filtered", - "id": "process.args", - "category": "process", - "type": "string", - "example": "[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]" - }, - { - "columnHeaderType": "not-filtered", - "id": "process.pid" - }, - { - "aggregatable": true, - "description": "Absolute path to the process executable.", - "columnHeaderType": "not-filtered", - "id": "process.parent.executable", - "category": "process", - "type": "string", - "example": "/usr/bin/ssh" - }, - { - "aggregatable": true, - "description": "Array of process arguments.\n\nMay be filtered to protect sensitive information.", - "columnHeaderType": "not-filtered", - "id": "process.parent.args", - "category": "process", - "type": "string", - "example": "[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]" - }, - { - "aggregatable": true, - "description": "Process id.", - "columnHeaderType": "not-filtered", - "id": "process.parent.pid", - "category": "process", - "type": "number", - "example": "4242" - }, - { - "aggregatable": true, - "description": "Short name or login of the user.", - "columnHeaderType": "not-filtered", - "id": "user.name", - "category": "user", - "type": "string", - "example": "albert" - }, - { - "aggregatable": true, - "description": "Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.", - "columnHeaderType": "not-filtered", - "id": "host.name", - "category": "host", - "type": "string" - } - ], - "dataProviders": [ - { - "excluded": false, - "and": [], - "kqlQuery": "", - "name": "{process.name}", - "queryMatch": { - "displayValue": null, - "field": "process.name", - "displayField": null, - "value": "{process.name}", - "operator": ":" - }, - "id": "timeline-1-8622010a-61fb-490d-b162-beac9c36a853", - "type": "template", - "enabled": true - } - ], - "dataViewId": "security-solution", - "description": "", - "eqlOptions": { - "eventCategoryField": "event.category", - "tiebreakerField": "", - "timestampField": "@timestamp", - "query": "", - "size": 100 - }, - "eventType": "all", - "excludedRowRendererIds": [], - "filters": [], - "kqlMode": "filter", - "kqlQuery": { - "filterQuery": { - "kuery": { - "kind": "kuery", - "expression": "" - }, - "serializedQuery": "" - } - }, - "indexNames": [], - "title": "Generic Process Timeline - Duplicate", - "timelineType": "template", - "templateTimelineVersion": 1, - "templateTimelineId": "94dd7443-97ea-4461-864d-fa96803ec111", - "dateRange": { - "start": "2021-04-06T07:57:57.922Z", - "end": "2021-04-07T07:57:57.922Z" - }, - "savedQueryId": null, - "sort": [ - { - "sortDirection": "desc", - "columnId": "@timestamp" - } - ], - "status": "active", - "created": 1617789914742, - "createdBy": "angela", - "updated": 1617790158569, - "updatedBy": "angela", - "favorite": [ - { - "favoriteDate": 1617790158569, - "keySearch": "YW5nZWxh", - "fullName": "Angela", - "userName": "angela" - } - ], - "eventIdToNoteIds": [], - "noteIds": [], - "notes": [], - "pinnedEventIds": [], - "pinnedEventsSaveObject": [] - } - } -} -``` - -## Delete timeline api - -#### DELETE /api/timeline - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` - -Content-Type: application/json - -kbn-version: 8.0.0 - -``` - -##### Request body - -```json -{ - "savedObjectIds": [savedObjectId1, savedObjectId2] -} -``` - -##### Response -```json -{"data":{"deleteTimeline":true}} -``` - -## Persist note api - -#### POST /api/note - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` - -##### Request body - -```json -{ - "note": { - "timelineId": {timeline id that the note is linked to}, - "eventId" (optional): {event id the note is linked to. Not available is it is a global note}, - "note"(optional): {note content}, - }, - "noteId"(optional): note savedObjectId, - "version" (optional): note savedObjectVersion -} -``` -##### Example -```json -{ - "noteId": null, - "version": null, - "note": { - "eventId": "Q9tqqXgBc4D54_cxJnHV", - "note": "note", - "timelineId": "1ec3b430-908e-11eb-94fa-c9122cbc0213" - } -} -``` - -##### Response -``` -{ - "data": { - "persistNote": { - "code": 200, - "message": "success", - "note": { - "noteId": "fe8f6980-97ad-11eb-862e-850f4426d3d0", - "version": "WzM1MDAyNSwzXQ==", - "eventId": "UNtqqXgBc4D54_cxIGi-", - "note": "event note", - "timelineId": "1ec3b430-908e-11eb-94fa-c9122cbc0213", - "created": 1617805912088, - "createdBy": "angela", - "updated": 1617805912088, - "updatedBy": "angela" - } - } - } -} -``` - -## Persist pinned event api - -#### POST /api/pinned_event - -##### Authorization - -Type: Basic Auth -username: Your Kibana username -password: Your Kibana password - - -##### Request header - -``` -Content-Type: application/json -kbn-version: 8.0.0 -``` - -##### Request body - -```json -{ - "eventId": {event which is pinned} - "pinnedEventId" (optional): {pinned event savedObjectId} - "timelineId": {timeline which this pinned event is linked to} -} -``` - -##### example - -``` -{ - "eventId":"UdtqqXgBc4D54_cxIGi", - "pinnedEventId":null, - "timelineId":"1ec3b430-908e-11eb-94fa-c9122cbc0213" -} -``` - -##### Response -```json -{ - "data": { - "persistPinnedEventOnTimeline": { - "pinnedEventId": "5b8f1720-97ae-11eb-862e-850f4426d3d0", - "version": "WzM1MDA1OSwzXQ==", - "eventId": "UdtqqXgBc4D54_cxIGi-", - "timelineId": "1ec3b430-908e-11eb-94fa-c9122cbc0213", - "created": 1617806068114, - "createdBy": "angela", - "updated": 1617806068114, - "updatedBy": "angela" - } - } -} -``` - - - diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts index ba727f6c0d707..72976ab3bf9f0 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts @@ -92,13 +92,7 @@ describe('clean draft timelines', () => { timelineType: req.body.timelineType, }); expect(response.status).toEqual(200); - expect(response.body).toEqual({ - data: { - persistTimeline: { - timeline: createTimelineWithTimelineId, - }, - }, - }); + expect(response.body).toEqual(createTimelineWithTimelineId); }); test('should return clean existing draft if draft available ', async () => { @@ -121,12 +115,6 @@ describe('clean draft timelines', () => { expect(mockGetTimeline).toHaveBeenCalled(); expect(response.status).toEqual(200); - expect(response.body).toEqual({ - data: { - persistTimeline: { - timeline: mockGetDraftTimelineValue, - }, - }, - }); + expect(response.body).toEqual(mockGetDraftTimelineValue); }); }); 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 fb6ffba7995b8..639a5a2ae0e8c 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 @@ -66,13 +66,7 @@ export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) = ); return response.ok({ - body: { - data: { - persistTimeline: { - timeline: cleanedDraftTimeline, - }, - }, - }, + body: cleanedDraftTimeline, }); } const templateTimelineData = @@ -91,17 +85,14 @@ export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) = if (newTimelineResponse.code === 200) { return response.ok({ - body: { - data: { - persistTimeline: { - timeline: newTimelineResponse.timeline, - }, - }, - }, + body: newTimelineResponse.timeline, + }); + } else { + return siemResponse.error({ + body: newTimelineResponse.message, + statusCode: newTimelineResponse.code, }); } - - return response.ok({}); } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts index 75c9b361bc78f..49e276f8a4c50 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts @@ -90,13 +90,7 @@ describe('get draft timelines', () => { }); expect(response.status).toEqual(200); - expect(response.body).toEqual({ - data: { - persistTimeline: { - timeline: createTimelineWithTimelineId, - }, - }, - }); + expect(response.body).toEqual(createTimelineWithTimelineId); }); test('should return an existing draft if available', async () => { @@ -110,13 +104,7 @@ describe('get draft timelines', () => { ); expect(mockPersistTimeline).not.toHaveBeenCalled(); expect(response.status).toEqual(200); - expect(response.body).toEqual({ - data: { - persistTimeline: { - timeline: mockGetDraftTimelineValue, - }, - }, - }); + expect(response.body).toEqual(mockGetDraftTimelineValue); }); }); }); 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 e83d2cc839db0..37db10f5393bc 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 @@ -49,13 +49,7 @@ export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) => if (draftTimeline?.savedObjectId) { return response.ok({ - body: { - data: { - persistTimeline: { - timeline: draftTimeline, - }, - }, - }, + body: draftTimeline, }); } @@ -65,18 +59,13 @@ export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) => }); if (newTimelineResponse.code === 200) { - return response.ok({ - body: { - data: { - persistTimeline: { - timeline: newTimelineResponse.timeline, - }, - }, - }, + return response.ok({ body: newTimelineResponse.timeline }); + } else { + return siemResponse.error({ + body: newTimelineResponse.message, + statusCode: newTimelineResponse.code, }); } - - return response.ok({}); } catch (err) { const error = transformError(err); 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 7308801030f4a..c2bc36e18c357 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 @@ -15,7 +15,7 @@ import { NOTE_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../utils/common'; -import { DeleteNoteRequestBody, type DeleteNoteResponse } from '../../../../../common/api/timeline'; +import { DeleteNoteRequestBody } from '../../../../../common/api/timeline'; import { deleteNote } from '../../saved_object/notes'; export const deleteNoteRoute = (router: SecuritySolutionPluginRouter) => { @@ -36,7 +36,7 @@ export const deleteNoteRoute = (router: SecuritySolutionPluginRouter) => { }, version: '2023-10-31', }, - async (context, request, response): Promise> => { + async (context, request, response): Promise => { const siemResponse = buildSiemResponse(response); try { @@ -56,9 +56,7 @@ export const deleteNoteRoute = (router: SecuritySolutionPluginRouter) => { noteIds, }); - return response.ok({ - body: { data: {} }, - }); + return response.ok(); } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts index 3a1ae1ba27e2f..dc8fffbc0a86d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts @@ -76,8 +76,7 @@ export const getNotesRoute = ( perPage: maxUnassociatedNotes, }; const res = await getAllSavedNote(frameworkRequest, options); - const body: GetNotesResponse = res ?? {}; - return response.ok({ body }); + return response.ok({ body: res }); } // searching for all the notes associated with a specific document id @@ -88,7 +87,7 @@ export const getNotesRoute = ( perPage: maxUnassociatedNotes, }; const res = await getAllSavedNote(frameworkRequest, options); - return response.ok({ body: res ?? {} }); + return response.ok({ body: res }); } // if savedObjectIds is provided, we will search for all the notes associated with the savedObjectIds @@ -106,8 +105,7 @@ export const getNotesRoute = ( perPage: maxUnassociatedNotes, }; const res = await getAllSavedNote(frameworkRequest, options); - const body: GetNotesResponse = res ?? {}; - return response.ok({ body }); + return response.ok({ body: res }); } // searching for all the notes associated with a specific saved object id @@ -120,8 +118,7 @@ export const getNotesRoute = ( perPage: maxUnassociatedNotes, }; const res = await getAllSavedNote(frameworkRequest, options); - const body: GetNotesResponse = res ?? {}; - return response.ok({ body }); + return response.ok({ body: res }); } // retrieving all the notes following the query parameters @@ -236,8 +233,7 @@ export const getNotesRoute = ( options.filter = nodeBuilder.and(filterKueryNodeArray); const res = await getAllSavedNote(frameworkRequest, options); - const body: GetNotesResponse = res ?? {}; - return response.ok({ body }); + return response.ok({ body: res }); } catch (err) { const error = transformError(err); const siemResponse = buildSiemResponse(response); 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 f9759444b26d8..12fed18a5c396 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 @@ -53,10 +53,9 @@ export const persistNoteRoute = (router: SecuritySolutionPluginRouter) => { note, overrideOwner: true, }); - const body: PersistNoteRouteResponse = { data: { persistNote: res } }; return response.ok({ - body, + body: res, }); } catch (err) { const error = transformError(err); 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 51b001c9ea29e..7dd66f86245ab 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 @@ -61,9 +61,7 @@ export const persistPinnedEventRoute = (router: SecuritySolutionPluginRouter) => ); return response.ok({ - body: { - data: { persistPinnedEventOnTimeline: res }, - }, + body: res, }); } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts index 502b43d4e347f..2241de72b3307 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts @@ -44,9 +44,17 @@ export const copyTimelineRoute = (router: SecuritySolutionPluginRouter) => { const frameworkRequest = await buildFrameworkRequest(context, request); const { timeline, timelineIdToCopy } = request.body; const copiedTimeline = await copyTimeline(frameworkRequest, timeline, timelineIdToCopy); - return response.ok({ - body: { data: { persistTimeline: copiedTimeline } }, - }); + + if (copiedTimeline.code === 200) { + return response.ok({ + body: copiedTimeline.timeline, + }); + } else { + return siemResponse.error({ + body: copiedTimeline.message, + statusCode: copiedTimeline.code, + }); + } } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts index a510f24a35637..67e7547b4b5a2 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts @@ -69,8 +69,8 @@ describe('create timelines', () => { beforeEach(async () => { jest.doMock('../../../saved_object/timelines', () => { return { - getTimeline: mockGetTimeline.mockReturnValue(null), persistTimeline: mockPersistTimeline.mockReturnValue({ + code: 200, timeline: createTimelineWithTimelineId, }), }; @@ -173,6 +173,7 @@ describe('create timelines', () => { return { getTimelineTemplateOrNull: mockGetTimeline.mockReturnValue(null), persistTimeline: mockPersistTimeline.mockReturnValue({ + code: 200, timeline: createTemplateTimelineWithTimelineId, }), }; 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 a91fefc20f934..e6af5abd78425 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 @@ -81,13 +81,16 @@ export const createTimelinesRoute = (router: SecuritySolutionPluginRouter) => { timelineVersion: version, }); - return response.ok({ - body: { - data: { - persistTimeline: newTimeline, - }, - }, - }); + if (newTimeline.code === 200) { + return response.ok({ + body: newTimeline.timeline, + }); + } else { + return siemResponse.error({ + statusCode: newTimeline.code, + body: newTimeline.message, + }); + } } else { return siemResponse.error( compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.create) || { 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 07cffb3e13bf5..4d64ab88c88fb 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 @@ -8,10 +8,7 @@ import type { IKibanaResponse } from '@kbn/core-http-server'; import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; -import { - DeleteTimelinesRequestBody, - type DeleteTimelinesResponse, -} from '../../../../../../common/api/timeline'; +import { DeleteTimelinesRequestBody } from '../../../../../../common/api/timeline'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_URL } from '../../../../../../common/constants'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -37,7 +34,7 @@ export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter) => { request: { body: buildRouteValidationWithZod(DeleteTimelinesRequestBody) }, }, }, - async (context, request, response): Promise> => { + async (context, request, response): Promise => { const siemResponse = buildSiemResponse(response); try { @@ -45,8 +42,7 @@ export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter) => { const { savedObjectIds, searchIds } = request.body; await deleteTimeline(frameworkRequest, savedObjectIds, searchIds); - const body: DeleteTimelinesResponse = { data: { deleteTimeline: true } }; - return response.ok({ body }); + return response.ok(); } catch (err) { const error = transformError(err); return siemResponse.error({ 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 a1ae2178fb6fd..75d61987e775b 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 @@ -20,7 +20,6 @@ import { type GetTimelineResponse, } from '../../../../../../common/api/timeline'; import { getTimelineTemplateOrNull, getTimelineOrNull } from '../../../saved_object/timelines'; -import type { ResolvedTimeline, TimelineResponse } from '../../../../../../common/api/timeline'; export const getTimelineRoute = (router: SecuritySolutionPluginRouter) => { router.versioned @@ -41,26 +40,33 @@ export const getTimelineRoute = (router: SecuritySolutionPluginRouter) => { }, }, async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + try { const frameworkRequest = await buildFrameworkRequest(context, request); const query = request.query ?? {}; const { template_timeline_id: templateTimelineId, id } = query; - let res: TimelineResponse | ResolvedTimeline | null = null; - if (templateTimelineId != null && id == null) { - res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); + const timeline = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); + if (timeline) { + return response.ok({ body: timeline }); + } } else if (templateTimelineId == null && id != null) { - res = await getTimelineOrNull(frameworkRequest, id); + const timelineOrNull = await getTimelineOrNull(frameworkRequest, id); + if (timelineOrNull) { + return response.ok({ body: timelineOrNull }); + } } else { throw new Error('please provide id or template_timeline_id'); } - return response.ok({ body: res ? { data: { getOneTimeline: res } } : {} }); + return siemResponse.error({ + statusCode: 404, + body: 'Could not find timeline', + }); } 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/get_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts index 01a3801ad8672..2ebcb4c38c37e 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 @@ -59,7 +59,6 @@ export const getTimelinesRoute = (router: SecuritySolutionPluginRouter) => { sortOrder, } : null; - let res = null; let totalCount = null; if (pageSize == null && pageIndex == null) { @@ -75,7 +74,7 @@ export const getTimelinesRoute = (router: SecuritySolutionPluginRouter) => { totalCount = allActiveTimelines.totalCount; } - res = await getAllTimeline( + const res = await getAllTimeline( frameworkRequest, onlyUserFavorite, { @@ -88,7 +87,7 @@ export const getTimelinesRoute = (router: SecuritySolutionPluginRouter) => { timelineType ); - return response.ok({ body: res ?? {} }); + return response.ok({ body: res }); } catch (err) { const error = transformError(err); const siemResponse = buildSiemResponse(response); 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 f66c5456c0396..3e40a7a7ebcb0 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 @@ -83,7 +83,7 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi if (res instanceof Error || typeof res === 'string') { throw res; } else { - return response.ok({ body: res ?? {} }); + return response.ok({ body: res }); } } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts index bb2ba80526d00..0c74d259bd228 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts @@ -69,6 +69,7 @@ describe('update timelines', () => { getTimelineOrNull: mockGetTimeline.mockReturnValue(mockGetTimelineValue), persistTimeline: mockPersistTimeline.mockReturnValue({ timeline: updateTimelineWithTimelineId.timeline, + code: 200, }), }; }); @@ -177,6 +178,7 @@ describe('update timelines', () => { }), persistTimeline: mockPersistTimeline.mockReturnValue({ timeline: updateTemplateTimelineWithTimelineId.timeline, + code: 200, }), }; }); 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 7ddea9bd5ffe7..340b8611901e5 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 @@ -73,13 +73,16 @@ export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter) => { timelineVersion: version, }); - return response.ok({ - body: { - data: { - persistTimeline: updatedTimeline, - }, - }, - }); + if (updatedTimeline.code === 200) { + return response.ok({ + body: updatedTimeline.timeline, + }); + } else { + return siemResponse.error({ + statusCode: updatedTimeline.code, + body: updatedTimeline.message, + }); + } } else { const error = compareTimelinesStatus.checkIsFailureCases(TimelineStatusActions.update); return siemResponse.error( 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 22d579229a73b..ed3531d8bd744 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 @@ -52,7 +52,7 @@ export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter) => { const { timelineId, templateTimelineId, templateTimelineVersion, timelineType } = request.body; - const timeline = await persistFavorite( + const persistFavoriteResponse = await persistFavorite( frameworkRequest, timelineId || null, templateTimelineId || null, @@ -60,15 +60,16 @@ export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter) => { timelineType || TimelineTypeEnum.default ); - const body: PersistFavoriteRouteResponse = { - data: { - persistFavorite: timeline, - }, - }; - - return response.ok({ - body, - }); + if (persistFavoriteResponse.code !== 200) { + return siemResponse.error({ + body: persistFavoriteResponse.message, + statusCode: persistFavoriteResponse.code, + }); + } else { + return response.ok({ + body: persistFavoriteResponse.favoriteTimeline, + }); + } } catch (err) { const error = transformError(err); return siemResponse.error({ 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 773e74faaaf46..8e6ff9e8adca7 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 @@ -21,7 +21,6 @@ import { type ResolveTimelineResponse, } from '../../../../../../common/api/timeline'; import { getTimelineTemplateOrNull, resolveTimelineOrNull } from '../../../saved_object/timelines'; -import type { SavedTimeline, ResolvedTimeline } from '../../../../../../common/api/timeline'; export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter) => { router.versioned @@ -42,28 +41,39 @@ export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter) => { }, }, async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + try { const frameworkRequest = await buildFrameworkRequest(context, request); const query = request.query ?? {}; const { template_timeline_id: templateTimelineId, id } = query; - let res: SavedTimeline | ResolvedTimeline | 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); + const timeline = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId); + if (timeline) { + return response.ok({ + body: { timeline, outcome: 'exactMatch' }, + }); + } } else if (templateTimelineId == null && id != null) { // In the event the objectId is defined, run the resolve call - res = await resolveTimelineOrNull(frameworkRequest, id); + const timelineOrNull = await resolveTimelineOrNull(frameworkRequest, id); + if (timelineOrNull) { + return response.ok({ + body: timelineOrNull, + }); + } } else { throw new Error('please provide id or template_timeline_id'); } - return response.ok({ body: res ? { data: res } : {} }); + return siemResponse.error({ + statusCode: 404, + body: 'Could not resolve timeline', + }); } 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/saved_object/notes/persist_notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/persist_notes.ts index 571e0f4da8616..3a211efc9565d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/persist_notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/persist_notes.ts @@ -6,7 +6,7 @@ */ import type { FrameworkRequest } from '../../../framework'; -import { persistNote } from './saved_object'; +import { persistNote, type InternalNoteResponse } from './saved_object'; import { getOverridableNote } from './get_overridable_note'; import type { Note } from '../../../../../common/api/timeline'; @@ -16,7 +16,7 @@ export const persistNotes = async ( existingNoteIds?: string[] | null, newNotes?: Note[], overrideOwner: boolean = true -) => { +): Promise => { return Promise.all( newNotes?.map(async (note) => { const newNote = await getOverridableNote( @@ -31,6 +31,6 @@ export const persistNotes = async ( note: newNote, overrideOwner, }); - }) ?? [] + }) ?? ([] as InternalNoteResponse[]) ); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts index ff353efe0fb53..7614ff1c1d1dc 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts @@ -6,7 +6,6 @@ */ import { failure } from 'io-ts/lib/PathReporter'; -import { getOr } from 'lodash/fp'; import { v1 as uuidv1 } from 'uuid'; import { pipe } from 'fp-ts/lib/pipeable'; @@ -81,6 +80,11 @@ export const getNotesByTimelineId = async ( return notesByTimelineId.notes; }; +export interface InternalNoteResponse extends ResponseNote { + message: string; + code: number; +} + export const persistNote = async ({ request, noteId, @@ -91,35 +95,18 @@ export const persistNote = async ({ noteId: string | null; note: BareNote | BareNoteWithoutExternalRefs; overrideOwner?: boolean; -}): Promise => { - try { - if (noteId == null) { - return await createNote({ - request, - noteId, - note, - overrideOwner, - }); - } - - // Update existing note - return await updateNote({ request, noteId, note, overrideOwner }); - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 403) { - const noteToReturn: Note = { - ...note, - noteId: uuidv1(), - version: '', - timelineId: '', - }; - return { - code: 403, - message: err.message, - note: noteToReturn, - }; - } - throw err; +}): Promise => { + if (noteId == null) { + return createNote({ + request, + noteId, + note, + overrideOwner, + }); } + + // Update existing note + return updateNote({ request, noteId, note, overrideOwner }); }; export const createNote = async ({ @@ -132,7 +119,7 @@ export const createNote = async ({ noteId: string | null; note: BareNote | BareNoteWithoutExternalRefs; overrideOwner?: boolean; -}): Promise => { +}): Promise => { const { savedObjects: { client: savedObjectsClient }, uiSettings: { client: uiSettingsClient }, @@ -203,7 +190,7 @@ export const updateNote = async ({ noteId: string; note: BareNote | BareNoteWithoutExternalRefs; overrideOwner?: boolean; -}): Promise => { +}): Promise => { const savedObjectsClient = (await request.context.core).savedObjects.client; const userInfo = request.user; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts index 5181a099ae7fb..e7537b94a5d62 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts @@ -6,7 +6,6 @@ */ import { failure } from 'io-ts/lib/PathReporter'; -import { getOr } from 'lodash/fp'; import { pipe } from 'fp-ts/lib/pipeable'; import { map, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; @@ -77,44 +76,24 @@ export const persistPinnedEventOnTimeline = async ( eventId: string, timelineId: string ): Promise => { - try { - if (pinnedEventId != null) { - // Delete Pinned Event on Timeline - await deletePinnedEventOnTimeline(request, [pinnedEventId]); - return null; - } - - const pinnedEvents = await getPinnedEventsInTimelineWithEventId(request, timelineId, eventId); - - // we already had this event pinned so let's just return the one we already had - if (pinnedEvents.length > 0) { - return { ...pinnedEvents[0], code: 200 }; - } - - return await createPinnedEvent({ - request, - eventId, - timelineId, - }); - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 404) { - /* - * Why we are doing that, because if it is not found for sure that it will be unpinned - * There is no need to bring back this error since we can assume that it is unpinned - */ - return null; - } - if (getOr(null, 'output.statusCode', err) === 403) { - return pinnedEventId != null - ? { - code: 403, - message: err.message, - pinnedEventId: eventId, - } - : null; - } - throw err; + if (pinnedEventId != null) { + // Delete Pinned Event on Timeline + await deletePinnedEventOnTimeline(request, [pinnedEventId]); + return { unpinned: true }; + } + + const pinnedEvents = await getPinnedEventsInTimelineWithEventId(request, timelineId, eventId); + + // we already had this event pinned so let's just return the one we already had + if (pinnedEvents.length > 0) { + return { ...pinnedEvents[0] }; } + + return createPinnedEvent({ + request, + eventId, + timelineId, + }); }; const getPinnedEventsInTimelineWithEventId = async ( @@ -172,7 +151,6 @@ const createPinnedEvent = async ({ // create Pinned Event on Timeline return { ...convertSavedObjectToSavedPinnedEvent(repopulatedSavedObject), - code: 200, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts index c3016164d4e7e..f8e99f2831ae3 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts @@ -298,61 +298,67 @@ export const getDraftTimeline = async ( return getAllSavedTimeline(request, options); }; +interface InternalPersistFavoriteResponse { + code: number; + message: string; + favoriteTimeline: FavoriteTimelineResponse; +} + export const persistFavorite = async ( request: FrameworkRequest, timelineId: string | null, templateTimelineId: string | null, templateTimelineVersion: number | null, timelineType: TimelineType -): Promise => { +): Promise => { const userName = request.user?.username ?? UNAUTHENTICATED_USER; const fullName = request.user?.full_name ?? ''; - try { - let timeline: SavedTimeline = {}; - if (timelineId != null) { - const { - eventIdToNoteIds, - notes, - noteIds, - pinnedEventIds, - pinnedEventsSaveObject, - savedObjectId, - version, - ...savedTimeline - } = await getBasicSavedTimeline(request, timelineId); - timelineId = savedObjectId; // eslint-disable-line no-param-reassign - timeline = savedTimeline; - } + let timeline: SavedTimeline = {}; + if (timelineId != null) { + const { + eventIdToNoteIds, + notes, + noteIds, + pinnedEventIds, + pinnedEventsSaveObject, + savedObjectId, + version, + ...savedTimeline + } = await getBasicSavedTimeline(request, timelineId); + timelineId = savedObjectId; // eslint-disable-line no-param-reassign + timeline = savedTimeline; + } - const userFavoriteTimeline = { - keySearch: userName != null ? convertStringToBase64(userName) : null, - favoriteDate: new Date().valueOf(), - fullName, - userName, - }; - if (timeline.favorite != null) { - const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( - (user) => user.userName === userName - ); - - timeline.favorite = - alreadyExistsTimelineFavoriteByUser > -1 - ? [ - ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), - ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), - ] - : [...timeline.favorite, userFavoriteTimeline]; - } else if (timeline.favorite == null) { - timeline.favorite = [userFavoriteTimeline]; - } + const userFavoriteTimeline = { + keySearch: userName != null ? convertStringToBase64(userName) : null, + favoriteDate: new Date().valueOf(), + fullName, + userName, + }; + if (timeline.favorite != null) { + const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( + (user) => user.userName === userName + ); - const persistResponse = await persistTimeline(request, timelineId, null, { - ...timeline, - templateTimelineId, - templateTimelineVersion, - timelineType, - }); - return { + timeline.favorite = + alreadyExistsTimelineFavoriteByUser > -1 + ? [ + ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), + ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), + ] + : [...timeline.favorite, userFavoriteTimeline]; + } else if (timeline.favorite == null) { + timeline.favorite = [userFavoriteTimeline]; + } + + const persistResponse = await persistTimeline(request, timelineId, null, { + ...timeline, + templateTimelineId, + templateTimelineVersion, + timelineType, + }); + return { + favoriteTimeline: { savedObjectId: persistResponse.timeline.savedObjectId, version: persistResponse.timeline.version, favorite: @@ -362,19 +368,10 @@ export const persistFavorite = async ( templateTimelineId, templateTimelineVersion, timelineType, - }; - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 403) { - return { - savedObjectId: '', - version: '', - favorite: [], - code: 403, - message: err.message, - }; - } - throw err; - } + }, + code: persistResponse.code, + message: persistResponse.message, + }; }; export interface InternalTimelineResponse { diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/draft_timeline.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/draft_timeline.ts index dafb08124b0e5..210cf28163f5c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/draft_timeline.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/draft_timeline.ts @@ -27,8 +27,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: 'default', }); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body.data && response.body; expect(savedObjectId).to.not.be.empty(); expect(version).to.not.be.empty(); @@ -49,7 +48,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType, templateTimelineId, templateTimelineVersion, - } = response.body.data && response.body.data.persistTimeline.timeline; + } = response.body.data && response.body; expect(savedObjectId).to.not.be.empty(); expect(version).to.not.be.empty(); @@ -72,7 +71,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { pinnedEventIds: initialPinnedEventIds, noteIds: initialNoteIds, version: initialVersion, - } = response.body.data && response.body.data.persistTimeline.timeline; + } = response.body.data && response.body; expect(initialPinnedEventIds).to.have.length(0, 'should not have any pinned events'); expect(initialNoteIds).to.have.length(0, 'should not have any notes'); @@ -107,7 +106,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { pinnedEventIds, noteIds, status: newStatus, - } = getTimelineRequest.body.data && getTimelineRequest.body.data.getOneTimeline; + } = getTimelineRequest.body.data && getTimelineRequest.body; expect(newStatus).to.be.equal('draft', 'status should still be draft'); expect(pinnedEventIds).to.have.length(1, 'should have one pinned event'); @@ -126,8 +125,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { pinnedEventIds: cleanedPinnedEventIds, noteIds: cleanedNoteIds, version: cleanedVersion, - } = cleanDraftTimelineRequest.body.data && - cleanDraftTimelineRequest.body.data.persistTimeline.timeline; + } = cleanDraftTimelineRequest.body.data && cleanDraftTimelineRequest.body; expect(cleanedPinnedEventIds).to.have.length(0, 'should not have pinned events anymore'); expect(cleanedNoteIds).to.have.length(0, 'should not have notes anymore'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/notes.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/notes.ts index 8e897509aaf98..f210eb88f9c4d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/notes.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/notes.ts @@ -35,8 +35,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { note: { note: myNote, timelineId: 'testTimelineId' }, }); - const { note, noteId, timelineId, version } = - response.body.data && response.body.data.persistNote.note; + const { note, noteId, timelineId, version } = response.body && response.body.note; expect(note).to.be(myNote); expect(noteId).to.not.be.empty(); @@ -56,8 +55,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { note: { note: myNote, timelineId: 'testTimelineId' }, }); - const { noteId, timelineId, version } = - response.body.data && response.body.data.persistNote.note; + const { noteId, timelineId, version } = response.body && response.body.note; const myNewNote = 'new world test'; const responseToTest = await supertest @@ -70,9 +68,9 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { note: { note: myNewNote, timelineId }, }); - expect(responseToTest.body.data!.persistNote.note.note).to.be(myNewNote); - expect(responseToTest.body.data!.persistNote.note.noteId).to.be(noteId); - expect(responseToTest.body.data!.persistNote.note.version).to.not.be.eql(version); + expect(responseToTest.body.note.note).to.be(myNewNote); + expect(responseToTest.body.note.noteId).to.be(noteId); + expect(responseToTest.body.note.version).to.not.be.eql(version); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/pinned_events.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/pinned_events.ts index 183c56fc2d8f3..3ef92b6a2f219 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/pinned_events.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/pinned_events.ts @@ -28,8 +28,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineId: 'testId', eventId: 'bv4QSGsB9v5HJNSH-7fi', }); - const { eventId, pinnedEventId, timelineId, version } = - response.body.data && response.body.data.persistPinnedEventOnTimeline; + const { eventId, pinnedEventId, timelineId, version } = response.body; expect(eventId).to.be('bv4QSGsB9v5HJNSH-7fi'); expect(pinnedEventId).to.not.be.empty(); @@ -39,7 +38,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { }); describe('unpin an event', () => { - it('returns null', async () => { + it('returns { unpinned: true }', async () => { const response = await supertest .patch(PINNED_EVENT_URL) .set('elastic-api-version', '2023-10-31') @@ -49,8 +48,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { eventId: 'bv4QSGsB9v5HJNSH-7fi', timelineId: 'testId', }); - const { eventId, pinnedEventId, timelineId } = - response.body.data && response.body.data.persistPinnedEventOnTimeline; + const { eventId, pinnedEventId, timelineId } = response.body; const responseToTest = await supertest .patch(PINNED_EVENT_URL) @@ -61,7 +59,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { eventId, timelineId, }); - expect(responseToTest.body.data!.persistPinnedEventOnTimeline).to.be(null); + expect(responseToTest.body).to.eql({ unpinned: true }); }); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/timeline.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/timeline.ts index 8029fce1c7b90..bd818ddc893a9 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/timeline.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/tests/timeline.ts @@ -6,10 +6,7 @@ */ import expect from '@kbn/expect'; -import { - TimelineResponse, - TimelineTypeEnum, -} from '@kbn/security-solution-plugin/common/api/timeline'; +import { TimelineTypeEnum } from '@kbn/security-solution-plugin/common/api/timeline'; import { TIMELINE_URL } from '@kbn/security-solution-plugin/common/constants'; import TestAgent from 'supertest/lib/agent'; import { FtrProviderContextWithSpaces } from '../../../../ftr_provider_context_with_spaces'; @@ -26,8 +23,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { it('Create a timeline just with a title', async () => { const titleToSaved = 'hello title'; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, title, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, title, version } = response.body; expect(title).to.be(titleToSaved); expect(savedObjectId).to.not.be.empty(); @@ -152,8 +148,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { sort, title, version, - } = - response.body.data && omitTypenameInTimeline(response.body.data.persistTimeline.timeline); + } = response.body; expect(columns.map((col: { id: string }) => col.id)).to.eql( timelineObject.columns.map((col) => col.id) @@ -172,8 +167,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { it('Update a timeline with a new title', async () => { const titleToSaved = 'hello title'; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body; const newTitle = 'new title'; @@ -187,11 +181,9 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { title: newTitle, }, }); - expect(responseToTest.body.data!.persistTimeline.timeline.savedObjectId).to.eql( - savedObjectId - ); - expect(responseToTest.body.data!.persistTimeline.timeline.title).to.be(newTitle); - expect(responseToTest.body.data!.persistTimeline.timeline.version).to.not.be.eql(version); + expect(responseToTest.body.savedObjectId).to.eql(savedObjectId); + expect(responseToTest.body.title).to.be(newTitle); + expect(responseToTest.body.version).to.not.be.eql(version); }); }); @@ -200,8 +192,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { const titleToSaved = 'hello title'; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body; const responseToTest = await supertest .patch('/api/timeline/_favorite') @@ -213,14 +204,12 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: TimelineTypeEnum.default, }); - expect(responseToTest.body.data!.persistFavorite.savedObjectId).to.be(savedObjectId); - expect(responseToTest.body.data!.persistFavorite.favorite.length).to.be(1); - expect(responseToTest.body.data!.persistFavorite.version).to.not.be.eql(version); - expect(responseToTest.body.data!.persistFavorite.templateTimelineId).to.be.eql(null); - expect(responseToTest.body.data!.persistFavorite.templateTimelineVersion).to.be.eql(null); - expect(responseToTest.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.default - ); + expect(responseToTest.body.savedObjectId).to.be(savedObjectId); + expect(responseToTest.body.favorite.length).to.be(1); + expect(responseToTest.body.version).to.not.be.eql(version); + expect(responseToTest.body.templateTimelineId).to.be.eql(null); + expect(responseToTest.body.templateTimelineVersion).to.be.eql(null); + expect(responseToTest.body.timelineType).to.be.eql(TimelineTypeEnum.default); }); it('to an existing timeline template', async () => { @@ -228,8 +217,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { const templateTimelineIdFromStore = 'f4a90a2d-365c-407b-9fef-c1dcb33a6ab3'; const templateTimelineVersionFromStore = 1; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body; const responseToTest = await supertest .patch('/api/timeline/_favorite') @@ -240,25 +228,20 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { templateTimelineVersion: templateTimelineVersionFromStore, timelineType: TimelineTypeEnum.template, }); - expect(responseToTest.body.data!.persistFavorite.savedObjectId).to.be(savedObjectId); - expect(responseToTest.body.data!.persistFavorite.favorite.length).to.be(1); - expect(responseToTest.body.data!.persistFavorite.version).to.not.be.eql(version); - expect(responseToTest.body.data!.persistFavorite.templateTimelineId).to.be.eql( - templateTimelineIdFromStore - ); - expect(responseToTest.body.data!.persistFavorite.templateTimelineVersion).to.be.eql( + expect(responseToTest.body.savedObjectId).to.be(savedObjectId); + expect(responseToTest.body.favorite.length).to.be(1); + expect(responseToTest.body.version).to.not.be.eql(version); + expect(responseToTest.body.templateTimelineId).to.be.eql(templateTimelineIdFromStore); + expect(responseToTest.body.templateTimelineVersion).to.be.eql( templateTimelineVersionFromStore ); - expect(responseToTest.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.template - ); + expect(responseToTest.body.timelineType).to.be.eql(TimelineTypeEnum.template); }); it('to Unfavorite an existing timeline', async () => { const titleToSaved = 'hello title'; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body; await supertest.patch('/api/timeline/_favorite').set('kbn-xsrf', 'true').send({ timelineId: savedObjectId, @@ -277,14 +260,12 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: TimelineTypeEnum.default, }); - expect(responseToTest.body.data!.persistFavorite.savedObjectId).to.be(savedObjectId); - expect(responseToTest.body.data!.persistFavorite.favorite).to.be.empty(); - expect(responseToTest.body.data!.persistFavorite.version).to.not.be.eql(version); - expect(responseToTest.body.data!.persistFavorite.templateTimelineId).to.be.eql(null); - expect(responseToTest.body.data!.persistFavorite.templateTimelineVersion).to.be.eql(null); - expect(responseToTest.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.default - ); + expect(responseToTest.body.savedObjectId).to.be(savedObjectId); + expect(responseToTest.body.favorite).to.be.empty(); + expect(responseToTest.body.version).to.not.be.eql(version); + expect(responseToTest.body.templateTimelineId).to.be.eql(null); + expect(responseToTest.body.templateTimelineVersion).to.be.eql(null); + expect(responseToTest.body.timelineType).to.be.eql(TimelineTypeEnum.default); }); it('to Unfavorite an existing timeline template', async () => { @@ -292,8 +273,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { const templateTimelineIdFromStore = 'f4a90a2d-365c-407b-9fef-c1dcb33a6ab3'; const templateTimelineVersionFromStore = 1; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId, version } = - response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId, version } = response.body; await supertest.patch('/api/timeline/_favorite').set('kbn-xsrf', 'true').send({ timelineId: savedObjectId, @@ -312,18 +292,14 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: TimelineTypeEnum.template, }); - expect(responseToTest.body.data!.persistFavorite.savedObjectId).to.be(savedObjectId); - expect(responseToTest.body.data!.persistFavorite.favorite).to.be.empty(); - expect(responseToTest.body.data!.persistFavorite.version).to.not.be.eql(version); - expect(responseToTest.body.data!.persistFavorite.templateTimelineId).to.be.eql( - templateTimelineIdFromStore - ); - expect(responseToTest.body.data!.persistFavorite.templateTimelineVersion).to.be.eql( + expect(responseToTest.body.savedObjectId).to.be(savedObjectId); + expect(responseToTest.body.favorite).to.be.empty(); + expect(responseToTest.body.version).to.not.be.eql(version); + expect(responseToTest.body.templateTimelineId).to.be.eql(templateTimelineIdFromStore); + expect(responseToTest.body.templateTimelineVersion).to.be.eql( templateTimelineVersionFromStore ); - expect(responseToTest.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.template - ); + expect(responseToTest.body.timelineType).to.be.eql(TimelineTypeEnum.template); }); it('to a timeline without a timelineId', async () => { @@ -337,14 +313,12 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: TimelineTypeEnum.default, }); - expect(response.body.data!.persistFavorite.savedObjectId).to.not.be.empty(); - expect(response.body.data!.persistFavorite.favorite.length).to.be(1); - expect(response.body.data!.persistFavorite.version).to.not.be.empty(); - expect(response.body.data!.persistFavorite.templateTimelineId).to.be.eql(null); - expect(response.body.data!.persistFavorite.templateTimelineVersion).to.be.eql(null); - expect(response.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.default - ); + expect(response.body.savedObjectId).to.not.be.empty(); + expect(response.body.favorite.length).to.be(1); + expect(response.body.version).to.not.be.empty(); + expect(response.body.templateTimelineId).to.be.eql(null); + expect(response.body.templateTimelineVersion).to.be.eql(null); + expect(response.body.timelineType).to.be.eql(TimelineTypeEnum.default); }); it('to a timeline template without a timelineId', async () => { @@ -361,18 +335,12 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { timelineType: TimelineTypeEnum.template, }); - expect(response.body.data!.persistFavorite.savedObjectId).to.not.be.empty(); - expect(response.body.data!.persistFavorite.favorite.length).to.be(1); - expect(response.body.data!.persistFavorite.version).to.not.be.empty(); - expect(response.body.data!.persistFavorite.templateTimelineId).to.be.eql( - templateTimelineIdFromStore - ); - expect(response.body.data!.persistFavorite.templateTimelineVersion).to.be.eql( - templateTimelineVersionFromStore - ); - expect(response.body.data!.persistFavorite.timelineType).to.be.eql( - TimelineTypeEnum.template - ); + expect(response.body.savedObjectId).to.not.be.empty(); + expect(response.body.favorite.length).to.be(1); + expect(response.body.version).to.not.be.empty(); + expect(response.body.templateTimelineId).to.be.eql(templateTimelineIdFromStore); + expect(response.body.templateTimelineVersion).to.be.eql(templateTimelineVersionFromStore); + expect(response.body.timelineType).to.be.eql(TimelineTypeEnum.template); }); }); @@ -380,7 +348,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { it('one timeline', async () => { const titleToSaved = 'hello title'; const response = await createBasicTimeline(supertest, titleToSaved); - const { savedObjectId } = response.body.data && response.body.data.persistTimeline.timeline; + const { savedObjectId } = response.body; const responseToTest = await supertest .delete(TIMELINE_URL) @@ -389,22 +357,16 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { savedObjectIds: [savedObjectId], }); - expect(responseToTest.body.data!.deleteTimeline).to.be(true); + expect(responseToTest.statusCode).to.be(200); }); it('multiple timelines', async () => { const titleToSaved = 'hello title'; const response1 = await createBasicTimeline(supertest, titleToSaved); - const savedObjectId1 = - response1.body.data && response1.body.data.persistTimeline.timeline - ? response1.body.data.persistTimeline.timeline.savedObjectId - : ''; + const savedObjectId1 = response1.body ? response1.body.savedObjectId : ''; const response2 = await createBasicTimeline(supertest, titleToSaved); - const savedObjectId2 = - response2.body.data && response2.body.data.persistTimeline.timeline - ? response2.body.data.persistTimeline.timeline.savedObjectId - : ''; + const savedObjectId2 = response2.body ? response2.body.savedObjectId : ''; const responseToTest = await supertest .delete(TIMELINE_URL) @@ -413,14 +375,8 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { savedObjectIds: [savedObjectId1, savedObjectId2], }); - expect(responseToTest.body.data!.deleteTimeline).to.be(true); + expect(responseToTest.status).to.be(200); }); }); }); } - -const omitTypename = (key: string, value: keyof TimelineResponse) => - key === '__typename' ? undefined : value; - -const omitTypenameInTimeline = (timeline: TimelineResponse) => - JSON.parse(JSON.stringify(timeline), omitTypename); diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/timeline/tests/timeline_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/timeline/tests/timeline_migrations.ts index 2e8a8fd4aaa94..a3a2bc27932c6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/timeline/tests/timeline_migrations.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/timeline/tests/timeline_migrations.ts @@ -68,9 +68,9 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(resolveWithSpaceApi) .query({ id: '1e2e9850-25f8-11ec-a981-b77847c6ef30' }); - expect(resp.body.data.outcome).to.be('aliasMatch'); - expect(resp.body.data.alias_target_id).to.not.be(undefined); - expect(resp.body.data.timeline.title).to.be('An awesome timeline'); + expect(resp.body.outcome).to.be('aliasMatch'); + expect(resp.body.alias_target_id).to.not.be(undefined); + expect(resp.body.timeline.title).to.be('An awesome timeline'); }); describe('notes', () => { @@ -79,7 +79,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(resolveWithSpaceApi) .query({ id: '1e2e9850-25f8-11ec-a981-b77847c6ef30' }); - expect(resp.body.data.timeline.notes[0].eventId).to.be('StU_UXwBAowmaxx6YdiS'); + expect(resp.body.timeline.notes[0].eventId).to.be('StU_UXwBAowmaxx6YdiS'); }); it('should return notes with the timelineId matching the resolved timeline id', async () => { @@ -87,12 +87,8 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(resolveWithSpaceApi) .query({ id: '1e2e9850-25f8-11ec-a981-b77847c6ef30' }); - expect(resp.body.data.timeline.notes[0].timelineId).to.be( - resp.body.data.timeline.savedObjectId - ); - expect(resp.body.data.timeline.notes[1].timelineId).to.be( - resp.body.data.timeline.savedObjectId - ); + expect(resp.body.timeline.notes[0].timelineId).to.be(resp.body.timeline.savedObjectId); + expect(resp.body.timeline.notes[1].timelineId).to.be(resp.body.timeline.savedObjectId); }); }); @@ -102,7 +98,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(resolveWithSpaceApi) .query({ id: '1e2e9850-25f8-11ec-a981-b77847c6ef30' }); - expect(resp.body.data.timeline.pinnedEventsSaveObject[0].eventId).to.be( + expect(resp.body.timeline.pinnedEventsSaveObject[0].eventId).to.be( 'StU_UXwBAowmaxx6YdiS' ); }); @@ -112,8 +108,8 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(resolveWithSpaceApi) .query({ id: '1e2e9850-25f8-11ec-a981-b77847c6ef30' }); - expect(resp.body.data.timeline.pinnedEventsSaveObject[0].timelineId).to.be( - resp.body.data.timeline.savedObjectId + expect(resp.body.timeline.pinnedEventsSaveObject[0].timelineId).to.be( + resp.body.timeline.savedObjectId ); }); }); @@ -161,7 +157,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); - expect(resp.body.data.getOneTimeline.notes[0].eventId).to.be('Edo00XsBEVtyvU-8LGNe'); + expect(resp.body.notes[0].eventId).to.be('Edo00XsBEVtyvU-8LGNe'); }); it('returns the timelineId in the response', async () => { @@ -169,12 +165,8 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); - expect(resp.body.data.getOneTimeline.notes[0].timelineId).to.be( - '6484cc90-126e-11ec-83d2-db1096c73738' - ); - expect(resp.body.data.getOneTimeline.notes[1].timelineId).to.be( - '6484cc90-126e-11ec-83d2-db1096c73738' - ); + expect(resp.body.notes[0].timelineId).to.be('6484cc90-126e-11ec-83d2-db1096c73738'); + expect(resp.body.notes[1].timelineId).to.be('6484cc90-126e-11ec-83d2-db1096c73738'); }); }); @@ -198,7 +190,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '8dc70950-1012-11ec-9ad3-2d7c6600c0f7' }); - expect(resp.body.data.getOneTimeline.title).to.be('Awesome Timeline'); + expect(resp.body.title).to.be('Awesome Timeline'); }); it('returns the savedQueryId in the response', async () => { @@ -206,7 +198,7 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '8dc70950-1012-11ec-9ad3-2d7c6600c0f7' }); - expect(resp.body.data.getOneTimeline.savedQueryId).to.be("It's me"); + expect(resp.body.savedQueryId).to.be("It's me"); }); }); describe('pinned events timelineId', () => { @@ -238,12 +230,8 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); - expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[0].eventId).to.be( - 'DNo00XsBEVtyvU-8LGNe' - ); - expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[1].eventId).to.be( - 'Edo00XsBEVtyvU-8LGNe' - ); + expect(resp.body.pinnedEventsSaveObject[0].eventId).to.be('DNo00XsBEVtyvU-8LGNe'); + expect(resp.body.pinnedEventsSaveObject[1].eventId).to.be('Edo00XsBEVtyvU-8LGNe'); }); it('returns the timelineId in the response', async () => { @@ -251,10 +239,10 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { .get(TIMELINE_URL) .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); - expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[0].timelineId).to.be( + expect(resp.body.pinnedEventsSaveObject[0].timelineId).to.be( '6484cc90-126e-11ec-83d2-db1096c73738' ); - expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[1].timelineId).to.be( + expect(resp.body.pinnedEventsSaveObject[1].timelineId).to.be( '6484cc90-126e-11ec-83d2-db1096c73738' ); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts index 438743dd01a70..f7f343c11b674 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts @@ -59,7 +59,7 @@ describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => deleteAlertsAndRules(); createTimeline() .then((response) => { - return response.body.data.persistTimeline.timeline.savedObjectId; + return response.body.savedObjectId; }) .as('timelineId'); visit(CREATE_RULE_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts index 6aded7a9a1f85..4ab4444ad968d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts @@ -72,8 +72,8 @@ describe( tags: ruleFields.ruleTags, false_positives: ruleFields.falsePositives, note: ruleFields.investigationGuide, - timeline_id: response.body.data.persistTimeline.timeline.savedObjectId, - timeline_title: response.body.data.persistTimeline.timeline.title ?? '', + timeline_id: response.body.savedObjectId, + timeline_title: response.body.title ?? '', interval: ruleFields.ruleInterval, from: `now-1h`, query: ruleFields.ruleQuery, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/non_default_space.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/non_default_space.cy.ts index 6b7cb3309e921..bef28e2112a4b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/non_default_space.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/non_default_space.cy.ts @@ -37,8 +37,8 @@ describe('Non-default space rule detail page', { tags: ['@ess'] }, function () { tags: ruleFields.ruleTags, false_positives: ruleFields.falsePositives, note: ruleFields.investigationGuide, - timeline_id: response.body.data.persistTimeline.timeline.savedObjectId, - timeline_title: response.body.data.persistTimeline.timeline.title ?? '', + timeline_id: response.body.savedObjectId, + timeline_title: response.body.title ?? '', interval: ruleFields.ruleInterval, from: `now-1h`, query: ruleFields.ruleQuery, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts index 68d14715d4641..56eab1675ac81 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts @@ -28,7 +28,7 @@ describe('attach timeline to case', { tags: ['@ess', '@serverless'] }, () => { deleteTimelines(); deleteCases(); createTimeline().then((response) => { - cy.wrap(response.body.data.persistTimeline.timeline).as('myTimeline'); + cy.wrap(response.body).as('myTimeline'); }); }); @@ -63,9 +63,7 @@ describe('attach timeline to case', { tags: ['@ess', '@serverless'] }, () => { login(); deleteTimelines(); deleteCases(); - createTimeline().then((response) => - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId') - ); + createTimeline().then((response) => cy.wrap(response.body.savedObjectId).as('timelineId')); createCase(getCase1()).then((response) => cy.wrap(response.body.id).as('caseId')); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts index 05d2cddc8eaca..12cb30a5775aa 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts @@ -67,7 +67,7 @@ describe('Cases', { tags: ['@ess', '@serverless'] }, () => { ...getCase1(), timeline: { ...getCase1().timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, + id: response.body.savedObjectId, }, }) .as('mycase') diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts index 78135fbd77235..0248f403b6107 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/overview/overview.cy.ts @@ -52,7 +52,7 @@ describe('Overview Page', { tags: ['@ess', '@serverless'] }, () => { describe('Favorite Timelines', { tags: ['@skipInServerless'] }, () => { it('should appear on overview page', () => { createTimeline() - .then((response) => response.body.data.persistTimeline.timeline.savedObjectId) + .then((response) => response.body.savedObjectId) .then((timelineId: string) => { favoriteTimeline({ timelineId, timelineType: 'default' }).then(() => { visitWithTimeRange(OVERVIEW_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts index 7531cf30a7752..b5189b846225f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts @@ -304,7 +304,7 @@ describe('url state', { tags: ['@ess', '@skipInServerless'] }, () => { cy.wait('@timeline').then(({ response }) => { closeTimeline(); cy.wrap(response?.statusCode).should('eql', 200); - const timelineId = response?.body.data.persistTimeline.timeline.savedObjectId; + const timelineId = response?.body.savedObjectId; visitWithTimeRange('/app/home'); visitWithTimeRange(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('exist'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts index c7078783466e9..6fb8492cf57c8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts @@ -52,7 +52,7 @@ describe('Ransomware Prevention Alerts', { tags: ['@ess', '@serverless'] }, () = deleteTimelines(); login(); createTimeline({ ...getTimeline(), query: 'event.code: "ransomware"' }).then((response) => { - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId'); + cy.wrap(response.body.savedObjectId).as('timelineId'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts index 9d2e1ac2e11a5..9d5b77919d548 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts @@ -100,11 +100,9 @@ describe('Timeline scope', { tags: ['@ess', '@serverless', '@skipInServerless'] beforeEach(() => { login(); deleteTimelines(); - createTimeline().then((response) => - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId') - ); + createTimeline().then((response) => cy.wrap(response.body.savedObjectId).as('timelineId')); createTimeline(getTimelineModifiedSourcerer()).then((response) => - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('auditbeatTimelineId') + cy.wrap(response.body.savedObjectId).as('auditbeatTimelineId') ); visitWithTimeRange(TIMELINES_URL); refreshUntilAlertsIndexExists(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts index 4e2da9bc9170b..5c4ad0f8d4712 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts @@ -83,7 +83,7 @@ describe.skip('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { closeTimeline(); cy.wait('@timeline').then(({ response }) => { - const { createdBy, savedObjectId } = response?.body.data.persistTimeline.timeline; + const { createdBy, savedObjectId } = response?.body; cy.log('Verify template shows on the table in the templates tab'); @@ -122,7 +122,7 @@ describe.skip('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { addNameToTimelineAndSave(savedName); cy.wait('@timeline').then(({ response }) => { - const { createdBy, savedObjectId } = response?.body.data.persistTimeline.timeline; + const { createdBy, savedObjectId } = response?.body; cy.log('Check that the template has been created correctly'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/export.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/export.cy.ts index 3c407a7506762..8c77cd61ebc29 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/export.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/export.cy.ts @@ -20,8 +20,8 @@ describe('Export timelines', { tags: ['@ess', '@serverless'] }, () => { deleteTimelines(); createTimelineTemplate().then((response) => { cy.wrap(response).as('templateResponse'); - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('templateId'); - cy.wrap(response.body.data.persistTimeline.timeline.title).as('templateTitle'); + cy.wrap(response.body.savedObjectId).as('templateId'); + cy.wrap(response.body.title).as('templateTitle'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts index 64ed8564626df..64ef95cd21b96 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts @@ -26,7 +26,7 @@ describe('Correlation tab', { tags: ['@ess', '@serverless'] }, () => { cy.intercept('PATCH', '/api/timeline').as('updateTimeline'); createTimeline().then((response) => { visit(TIMELINES_URL); - openTimeline(response.body.data.persistTimeline.timeline.savedObjectId); + openTimeline(response.body.savedObjectId); addEqlToTimeline(eql); saveTimeline(); cy.wait('@updateTimeline'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts index 75d0ddd63dc2a..b4d0f1b0e0235 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts @@ -53,8 +53,7 @@ const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186'; const TIMELINE_REQ_WITH_SAVED_SEARCH = 'TIMELINE_REQ_WITH_SAVED_SEARCH'; const TIMELINE_PATCH_REQ = 'TIMELINE_PATCH_REQ'; -const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH = - 'response.body.data.persistTimeline.timeline.savedObjectId'; +const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH = 'response.body.savedObjectId'; const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"'; const handleIntercepts = () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/export.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/export.cy.ts index 0800c2b610a27..ba0ec381ddeb8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/export.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/export.cy.ts @@ -34,11 +34,11 @@ describe.skip('Export timelines', { tags: ['@ess', '@serverless'] }, () => { }).as('export'); createTimeline().then((response) => { cy.wrap(response).as('timelineResponse1'); - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId1'); + cy.wrap(response.body.savedObjectId).as('timelineId1'); }); createTimeline().then((response) => { cy.wrap(response).as('timelineResponse2'); - cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId2'); + cy.wrap(response.body.savedObjectId).as('timelineId2'); }); visit(TIMELINES_URL); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts index 7eead20dcb8c1..de32f28223214 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts @@ -33,7 +33,7 @@ describe('Timeline notes tab', { tags: ['@ess', '@serverless'] }, () => { deleteTimelines(); createTimeline(getTimelineNonValidQuery()) - .then((response) => response.body.data.persistTimeline.timeline.savedObjectId) + .then((response) => response.body.savedObjectId) .then((timelineId: string) => { login(); visitTimeline(timelineId); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts index 73b6fff4fa8ec..feee9a36168b2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts @@ -35,7 +35,7 @@ describe('Open timeline modal', { tags: ['@serverless', '@ess'] }, () => { login(); visit(TIMELINES_URL); createTimeline() - .then((response) => response.body.data.persistTimeline.timeline.savedObjectId) + .then((response) => response.body.savedObjectId) .then((timelineId: string) => { refreshTimelinesUntilTimeLinePresent(timelineId) // This cy.wait is here because we cannot do a pipe on a timeline as that will introduce multiple URL diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts index ebc3591adfe15..9ae02b1223f00 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts @@ -77,9 +77,7 @@ describe('Row renderers', { tags: ['@ess', '@serverless'] }, () => { addNameToTimelineAndSave('Test'); cy.wait('@excludedNetflow').then((interception) => { - expect( - interception?.response?.body.data.persistTimeline.timeline.excludedRowRendererIds - ).to.contain('netflow'); + expect(interception?.response?.body.excludedRowRendererIds).to.contain('netflow'); }); // open modal, filter and check @@ -93,9 +91,7 @@ describe('Row renderers', { tags: ['@ess', '@serverless'] }, () => { saveTimeline(); cy.wait('@includedNetflow').then((interception) => { - expect( - interception?.response?.body.data.persistTimeline.timeline.excludedRowRendererIds - ).not.to.contain('netflow'); + expect(interception?.response?.body.excludedRowRendererIds).not.to.contain('netflow'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts index 617bfce6b5caa..25a21ecde3b9c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts @@ -66,7 +66,7 @@ describe('Timeline search and filters', { tags: ['@ess', '@serverless'] }, () => addNameToTimelineAndSave('Test'); cy.wait('@update').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response?.body.data.persistTimeline.timeline.kqlMode).should('eql', 'filter'); + cy.wrap(response?.body.kqlMode).should('eql', 'filter'); cy.get(ADD_FILTER).should('exist'); }); }); @@ -76,7 +76,7 @@ describe('Timeline search and filters', { tags: ['@ess', '@serverless'] }, () => addNameToTimelineAndSave('Test'); cy.wait('@update').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response?.body.data.persistTimeline.timeline.kqlMode).should('eql', 'search'); + cy.wrap(response?.body.kqlMode).should('eql', 'search'); cy.get(ADD_FILTER).should('not.exist'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/timelines_table.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/timelines_table.cy.ts index fe0e60afa785c..24c4c5ded0e88 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/timelines_table.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/timelines_table.cy.ts @@ -40,7 +40,7 @@ describe.skip('timeline overview search', { tags: ['@ess', '@serverless'] }, () // create timeline and favorite it // we're doing it through the UI because doing it through the API currently has a problem on MKI environment createTimeline(mockFavoritedTimeline) - .then((response) => response.body.data.persistTimeline.timeline.savedObjectId) + .then((response) => response.body.savedObjectId) .then((timelineId) => { refreshTimelinesUntilTimeLinePresent(timelineId); openTimelineById(timelineId); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/url_state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/url_state.cy.ts index df80bb4c50363..3fa1d2dc5902f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/url_state.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/url_state.cy.ts @@ -24,8 +24,8 @@ describe('Open timeline', { tags: ['@serverless', '@ess'] }, () => { deleteTimelines(); visit(TIMELINES_URL); createTimeline().then((response) => { - timelineSavedObjectId = response.body.data.persistTimeline.timeline.savedObjectId; - return response.body.data.persistTimeline.timeline.savedObjectId; + timelineSavedObjectId = response.body.savedObjectId; + return response.body.savedObjectId; }); createRule(getNewRule()); visitWithTimeRange(ALERTS_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/objects/timeline.ts b/x-pack/test/security_solution_cypress/cypress/objects/timeline.ts index 1aea82de6612d..aab0cbd06b54b 100644 --- a/x-pack/test/security_solution_cypress/cypress/objects/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/objects/timeline.ts @@ -72,7 +72,7 @@ export const expectedExportedTimelineTemplate = ( templateResponse: Cypress.Response, username: string ) => { - const timelineTemplateBody = templateResponse.body.data.persistTimeline.timeline; + const timelineTemplateBody = templateResponse.body; return { savedObjectId: timelineTemplateBody.savedObjectId, @@ -118,7 +118,7 @@ export const expectedExportedTimeline = ( timelineResponse: Cypress.Response, username: string ) => { - const timelineBody = timelineResponse.body.data.persistTimeline.timeline; + const timelineBody = timelineResponse.body; return { savedObjectId: timelineBody.savedObjectId, diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts index 055d98a2efcfd..b33ed2e14235b 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts @@ -6,7 +6,6 @@ */ import type { - DeleteTimelinesResponse, GetTimelinesResponse, PatchTimelineResponse, } from '@kbn/security-solution-plugin/common/api/timeline'; @@ -168,7 +167,7 @@ export const getAllTimelines = () => export const deleteTimelines = () => { getAllTimelines().then(($timelines) => { const savedObjectIds = $timelines.body.timeline.map((timeline) => timeline.savedObjectId); - rootRequest({ + rootRequest({ method: 'DELETE', url: 'api/timeline', body: { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts index 580b96bfd2340..e40563c78c8af 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts @@ -86,21 +86,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); await pageObjects.timeline.navigateToTimelineList(); - await pageObjects.timeline.openTimelineById( - timeline.data.persistTimeline.timeline.savedObjectId - ); + await pageObjects.timeline.openTimelineById(timeline.savedObjectId); await pageObjects.timeline.setDateRange('Last 1 year'); await pageObjects.timeline.waitForEvents(60_000 * 2); }); after(async () => { if (timeline) { - log.info( - `Cleaning up created timeline [${timeline.data.persistTimeline.timeline.title} - ${timeline.data.persistTimeline.timeline.savedObjectId}]` - ); - await timelineTestService.deleteTimeline( - timeline.data.persistTimeline.timeline.savedObjectId - ); + log.info(`Cleaning up created timeline [${timeline.title} - ${timeline.savedObjectId}]`); + await timelineTestService.deleteTimeline(timeline.savedObjectId); } }); diff --git a/x-pack/test/security_solution_ftr/services/timeline/index.ts b/x-pack/test/security_solution_ftr/services/timeline/index.ts index 8195f63ffc246..4288e073519c4 100644 --- a/x-pack/test/security_solution_ftr/services/timeline/index.ts +++ b/x-pack/test/security_solution_ftr/services/timeline/index.ts @@ -9,7 +9,6 @@ import { Response } from 'superagent'; import { EndpointError } from '@kbn/security-solution-plugin/common/endpoint/errors'; import { TIMELINE_DRAFT_URL, TIMELINE_URL } from '@kbn/security-solution-plugin/common/constants'; import { - DeleteTimelinesResponse, GetDraftTimelinesResponse, PatchTimelineResponse, SavedTimeline, @@ -58,15 +57,13 @@ export class TimelineTestService extends FtrService { */ async createTimeline(title: string): Promise { // Create a new timeline draft - const createdTimeline = ( - await this.supertest - .post(TIMELINE_DRAFT_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ timelineType: 'default' }) - .then(this.getHttpResponseFailureHandler()) - .then((response) => response.body as GetDraftTimelinesResponse) - ).data.persistTimeline.timeline; + const createdTimeline = await this.supertest + .post(TIMELINE_DRAFT_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ timelineType: 'default' }) + .then(this.getHttpResponseFailureHandler()) + .then((response) => response.body as GetDraftTimelinesResponse); this.log.info('Draft timeline:'); this.log.indent(4, () => { @@ -137,7 +134,7 @@ export class TimelineTestService extends FtrService { savedObjectIds: Array.isArray(id) ? id : [id], }) .then(this.getHttpResponseFailureHandler()) - .then((response) => response.body as DeleteTimelinesResponse); + .then((response) => response.body); } /** @@ -183,7 +180,7 @@ export class TimelineTestService extends FtrService { const { expression, esQuery } = this.getEndpointAlertsKqlQuery(endpointAgentId); const updatedTimeline = await this.updateTimeline( - newTimeline.data.persistTimeline.timeline.savedObjectId, + newTimeline.savedObjectId, { title, kqlQuery: { @@ -197,7 +194,7 @@ export class TimelineTestService extends FtrService { }, savedSearchId: null, }, - newTimeline.data.persistTimeline.timeline.version + newTimeline.version ); return updatedTimeline;