mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[SecuritySolution][Timeline] Refactor timeline HTTP API (#200633)
## Summary The timeline API endpoints are currently implemented from a mix of HTTP and GraphQL practices. Since GraphQL has been removed for a long time now, we should make sure the endpoints conform to HTTP best practices. This will allow us to simplify the API- and client-logic. Further, third-parties accessing these APIs will have an easier time integrating. ### Usage of HTTP status codes Depending on the error, the API endpoints currently return a `200` with `{ code: 404, message: '(...)' }` or an actual HTTP error response with e.g. `403` and the message in the body. The practice of returning 200s with embedded error codes comes from GraphQL, where error codes are always embedded. Example of a current HTTP response of a failed timeline request: ``` HTTP status: 200 HTTP body: { "error_code": 409, "messsage": "there was a conflict" } ``` Going forward, all endpoints should return proper error codes and embed the error messages in the response body. ``` HTTP status: 409 HTTP body: { "messsage": "there was a conflict" } ``` ### Removal of `{}` responses Some timeline endpoints might return empty objects in case they were not able to resolve/retrieve some SOs. The empty object implies a `404` response. This creates complications on the client that now have to provide extra logic for how to interpret empty objects. Example of a current request of one of the endpoints that allows empty responses. ``` HTTP status: 200 {} ``` The absence of an object, on some of the listed endpoints, indicates a 404 or the top-level or embedded saved object. Going forward, the endpoints should not return empty objects and instead return the proper HTTP error code (e.g. `404`) or a success code. ``` HTTP status: 404 ``` ### No more nested bodies Another relic of the GraphQL time is the nesting of request bodies like this: ``` HTTP status: 200 HTTP body: { "data": { "persistTimeline": { (actual timeline object) } } } ``` Combined with sometimes returning empty objects and potentially returning a status code in the body, makes it overly complicated for clients to reason about the response. Going forward, the actual object(s) should be returned as a top-level JSON object, omitting `data.persistX`. ``` HTTP status: 200 HTTP body: { (actual timeline object) } ``` ### Checklist - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
50236c4677
commit
82108f134e
101 changed files with 1131 additions and 3522 deletions
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<DeleteNoteResponse>({
|
||||
.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<DeleteTimelinesResponse>({
|
||||
.request({
|
||||
path: '/api/timeline',
|
||||
headers: {
|
||||
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
|
||||
|
|
|
@ -30,8 +30,3 @@ export const DeleteNoteRequestBody = z.union([
|
|||
.nullable(),
|
||||
]);
|
||||
export type DeleteNoteRequestBodyInput = z.input<typeof DeleteNoteRequestBody>;
|
||||
|
||||
export type DeleteNoteResponse = z.infer<typeof DeleteNoteResponse>;
|
||||
export const DeleteNoteResponse = z.object({
|
||||
data: z.object({}).optional(),
|
||||
});
|
||||
|
|
|
@ -37,10 +37,3 @@ paths:
|
|||
responses:
|
||||
'200':
|
||||
description: Indicates the note was successfully deleted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
|
|
|
@ -25,10 +25,3 @@ export const DeleteTimelinesRequestBody = z.object({
|
|||
searchIds: z.array(z.string()).optional(),
|
||||
});
|
||||
export type DeleteTimelinesRequestBodyInput = z.input<typeof DeleteTimelinesRequestBody>;
|
||||
|
||||
export type DeleteTimelinesResponse = z.infer<typeof DeleteTimelinesResponse>;
|
||||
export const DeleteTimelinesResponse = z.object({
|
||||
data: z.object({
|
||||
deleteTimeline: z.boolean(),
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -60,4 +60,4 @@ export const GetNotesRequestQuery = z.object({
|
|||
export type GetNotesRequestQueryInput = z.input<typeof GetNotesRequestQuery>;
|
||||
|
||||
export type GetNotesResponse = z.infer<typeof GetNotesResponse>;
|
||||
export const GetNotesResponse = z.union([GetNotesResult, z.object({})]);
|
||||
export const GetNotesResponse = GetNotesResult;
|
||||
|
|
|
@ -66,9 +66,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/GetNotesResult'
|
||||
- type: object
|
||||
$ref: '#/components/schemas/GetNotesResult'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
|
|
|
@ -32,11 +32,4 @@ export const GetTimelineRequestQuery = z.object({
|
|||
export type GetTimelineRequestQueryInput = z.input<typeof GetTimelineRequestQuery>;
|
||||
|
||||
export type GetTimelineResponse = z.infer<typeof GetTimelineResponse>;
|
||||
export const GetTimelineResponse = z.union([
|
||||
z.object({
|
||||
data: z.object({
|
||||
getOneTimeline: TimelineResponse,
|
||||
}),
|
||||
}),
|
||||
z.object({}).strict(),
|
||||
]);
|
||||
export const GetTimelineResponse = TimelineResponse;
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -309,8 +309,6 @@ export type FavoriteTimelineResponse = z.infer<typeof FavoriteTimelineResponse>;
|
|||
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<typeof PersistTimelineResponse>;
|
||||
export const PersistTimelineResponse = z.object({
|
||||
data: z.object({
|
||||
persistTimeline: z.object({
|
||||
timeline: TimelineResponse,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export const PersistTimelineResponse = TimelineResponse;
|
||||
|
||||
export type BareNoteWithoutExternalRefs = z.infer<typeof BareNoteWithoutExternalRefs>;
|
||||
export const BareNoteWithoutExternalRefs = z.object({
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -28,8 +28,4 @@ export const PersistFavoriteRouteRequestBody = z.object({
|
|||
export type PersistFavoriteRouteRequestBodyInput = z.input<typeof PersistFavoriteRouteRequestBody>;
|
||||
|
||||
export type PersistFavoriteRouteResponse = z.infer<typeof PersistFavoriteRouteResponse>;
|
||||
export const PersistFavoriteRouteResponse = z.object({
|
||||
data: z.object({
|
||||
persistFavorite: FavoriteTimelineResponse,
|
||||
}),
|
||||
});
|
||||
export const PersistFavoriteRouteResponse = FavoriteTimelineResponse;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -20,8 +20,6 @@ import { BareNote, Note } from '../model/components.gen';
|
|||
|
||||
export type ResponseNote = z.infer<typeof ResponseNote>;
|
||||
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<typeof PersistNoteRouteRequestBody>;
|
||||
|
||||
export type PersistNoteRouteResponse = z.infer<typeof PersistNoteRouteResponse>;
|
||||
export const PersistNoteRouteResponse = z.object({
|
||||
data: z.object({
|
||||
persistNote: ResponseNote,
|
||||
}),
|
||||
});
|
||||
export const PersistNoteRouteResponse = ResponseNote;
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -18,16 +18,12 @@ import { z } from '@kbn/zod';
|
|||
|
||||
import { PinnedEvent } from '../model/components.gen';
|
||||
|
||||
export type PinnedEventBaseResponseBody = z.infer<typeof PinnedEventBaseResponseBody>;
|
||||
export const PinnedEventBaseResponseBody = z.object({
|
||||
code: z.number(),
|
||||
message: z.string().optional(),
|
||||
});
|
||||
|
||||
export type PersistPinnedEventResponse = z.infer<typeof PersistPinnedEventResponse>;
|
||||
export const PersistPinnedEventResponse = z.union([
|
||||
PinnedEvent.merge(PinnedEventBaseResponseBody),
|
||||
z.object({}).nullable(),
|
||||
PinnedEvent,
|
||||
z.object({
|
||||
unpinned: z.boolean(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type PersistPinnedEventRouteRequestBody = z.infer<typeof PersistPinnedEventRouteRequestBody>;
|
||||
|
@ -41,8 +37,4 @@ export type PersistPinnedEventRouteRequestBodyInput = z.input<
|
|||
>;
|
||||
|
||||
export type PersistPinnedEventRouteResponse = z.infer<typeof PersistPinnedEventRouteResponse>;
|
||||
export const PersistPinnedEventRouteResponse = z.object({
|
||||
data: z.object({
|
||||
persistPinnedEventOnTimeline: PersistPinnedEventResponse,
|
||||
}),
|
||||
});
|
||||
export const PersistPinnedEventRouteResponse = PersistPinnedEventResponse;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -32,9 +32,4 @@ export const ResolveTimelineRequestQuery = z.object({
|
|||
export type ResolveTimelineRequestQueryInput = z.input<typeof ResolveTimelineRequestQuery>;
|
||||
|
||||
export type ResolveTimelineResponse = z.infer<typeof ResolveTimelineResponse>;
|
||||
export const ResolveTimelineResponse = z.union([
|
||||
z.object({
|
||||
data: ResolvedTimeline,
|
||||
}),
|
||||
z.object({}).strict(),
|
||||
]);
|
||||
export const ResolveTimelineResponse = ResolvedTimeline;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
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: ':',
|
||||
},
|
||||
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":"*"}}',
|
||||
},
|
||||
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: '*',
|
||||
},
|
||||
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: [],
|
||||
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 = {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
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,
|
||||
},
|
||||
{
|
||||
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',
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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<DeleteNoteResponse>(NOTE_URL, {
|
||||
const response = await KibanaServices.get().http.delete(NOTE_URL, {
|
||||
body: JSON.stringify({ noteIds }),
|
||||
version: '2023-10-31',
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
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: 'message',
|
||||
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: '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,
|
||||
},
|
||||
{
|
||||
aggregatable: null,
|
||||
category: null,
|
||||
columnHeaderType: 'not-filtered',
|
||||
description: null,
|
||||
example: null,
|
||||
indexes: null,
|
||||
id: 'host.name',
|
||||
name: null,
|
||||
searchable: null,
|
||||
type: null,
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
dateRange: {
|
||||
start: '2020-10-27T14:22:11.809Z',
|
||||
end: '2020-11-03T14:22:11.809Z',
|
||||
__typename: 'DateRangePickerResult',
|
||||
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,
|
||||
},
|
||||
description: '',
|
||||
eventType: 'all',
|
||||
eventIdToNoteIds: [],
|
||||
excludedRowRendererIds: [],
|
||||
favorite: [],
|
||||
filters: [],
|
||||
kqlMode: 'filter',
|
||||
kqlQuery: {
|
||||
filterQuery: {
|
||||
kuery: { kind: 'kuery', expression: '', __typename: 'KueryFilterQueryResult' },
|
||||
serializedQuery: '',
|
||||
__typename: 'SerializedKueryQueryResult',
|
||||
{
|
||||
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: ':',
|
||||
},
|
||||
__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',
|
||||
{
|
||||
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: ':*',
|
||||
},
|
||||
and: [],
|
||||
},
|
||||
created: 1604413368243,
|
||||
createdBy: 'angela',
|
||||
updated: 1604413368243,
|
||||
updatedBy: 'angela',
|
||||
version: 'WzQwMywxXQ==',
|
||||
__typename: 'TimelineResult',
|
||||
],
|
||||
dateRange: {
|
||||
start: '2020-10-27T14:22:11.809Z',
|
||||
end: '2020-11-03T14:22:11.809Z',
|
||||
},
|
||||
outcome: 'exactMatch',
|
||||
description: '',
|
||||
eventType: 'all',
|
||||
eventIdToNoteIds: [],
|
||||
excludedRowRendererIds: [],
|
||||
favorite: [],
|
||||
filters: [],
|
||||
kqlMode: 'filter',
|
||||
kqlQuery: {
|
||||
filterQuery: {
|
||||
kuery: { kind: 'kuery', expression: '' },
|
||||
serializedQuery: '',
|
||||
},
|
||||
},
|
||||
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',
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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<typeof updateIsFavorite>;
|
||||
|
||||
|
@ -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({
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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<typeof addNote | typeof addNoteToEvent>;
|
||||
|
||||
|
@ -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({
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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<typeof pinEvent | typeof unPinEvent>;
|
||||
|
||||
|
@ -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({
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<IKibanaResponse<DeleteNoteResponse>> => {
|
||||
async (context, request, response): Promise<IKibanaResponse> => {
|
||||
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({
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -61,9 +61,7 @@ export const persistPinnedEventRoute = (router: SecuritySolutionPluginRouter) =>
|
|||
);
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
data: { persistPinnedEventOnTimeline: res },
|
||||
},
|
||||
body: res,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -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) || {
|
||||
|
|
|
@ -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<IKibanaResponse<DeleteTimelinesResponse>> => {
|
||||
async (context, request, response): Promise<IKibanaResponse> => {
|
||||
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({
|
||||
|
|
|
@ -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<IKibanaResponse<GetTimelineResponse>> => {
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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<IKibanaResponse<ResolveTimelineResponse>> => {
|
||||
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,
|
||||
|
|
|
@ -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<InternalNoteResponse[]> => {
|
||||
return Promise.all(
|
||||
newNotes?.map(async (note) => {
|
||||
const newNote = await getOverridableNote(
|
||||
|
@ -31,6 +31,6 @@ export const persistNotes = async (
|
|||
note: newNote,
|
||||
overrideOwner,
|
||||
});
|
||||
}) ?? []
|
||||
}) ?? ([] as InternalNoteResponse[])
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<ResponseNote> => {
|
||||
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<InternalNoteResponse> => {
|
||||
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<ResponseNote> => {
|
||||
}): Promise<InternalNoteResponse> => {
|
||||
const {
|
||||
savedObjects: { client: savedObjectsClient },
|
||||
uiSettings: { client: uiSettingsClient },
|
||||
|
@ -203,7 +190,7 @@ export const updateNote = async ({
|
|||
noteId: string;
|
||||
note: BareNote | BareNoteWithoutExternalRefs;
|
||||
overrideOwner?: boolean;
|
||||
}): Promise<ResponseNote> => {
|
||||
}): Promise<InternalNoteResponse> => {
|
||||
const savedObjectsClient = (await request.context.core).savedObjects.client;
|
||||
const userInfo = request.user;
|
||||
|
||||
|
|
|
@ -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<PersistPinnedEventResponse> => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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<FavoriteTimelineResponse> => {
|
||||
): Promise<InternalPersistFavoriteResponse> => {
|
||||
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
|
||||
);
|
||||
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];
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -72,7 +72,7 @@ export const expectedExportedTimelineTemplate = (
|
|||
templateResponse: Cypress.Response<PersistTimelineResponse>,
|
||||
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<PersistTimelineResponse>,
|
||||
username: string
|
||||
) => {
|
||||
const timelineBody = timelineResponse.body.data.persistTimeline.timeline;
|
||||
const timelineBody = timelineResponse.body;
|
||||
|
||||
return {
|
||||
savedObjectId: timelineBody.savedObjectId,
|
||||
|
|
|
@ -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<DeleteTimelinesResponse>({
|
||||
rootRequest({
|
||||
method: 'DELETE',
|
||||
url: 'api/timeline',
|
||||
body: {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue