[8.x] [Threat Hunting Investigations] Migrate all timeline routes to OpenAPI types (#190238) (#194606)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Threat Hunting Investigations] Migrate all timeline routes to
OpenAPI types (#190238)](https://github.com/elastic/kibana/pull/190238)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Jan
Monschke","email":"jan.monschke@elastic.co"},"sourceCommit":{"committedDate":"2024-09-30T18:55:23Z","message":"[Threat
Hunting Investigations] Migrate all timeline routes to OpenAPI types
(#190238)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/security-team/issues/10235\r\nfixes:
https://github.com/elastic/security-team/issues/10237\r\n\r\nThis is the
final PR for migrating over all timeline-related schemas and\r\ntypes to
the new generated zod schemas from our OpenAPI specs.
(see\r\nhttps://github.com/elastic/security-team/issues/10110)\r\nOn top
of moving to the new schemas/types, this PR also cleans up usage\r\nof
now outdated types.\r\n\r\nI'm aware of the size of this PR but rest
assured, the changes are easy\r\nto review and for most teams, only a
handful of files need to be\r\nreviewed:\r\n\r\n```markdown\r\n###
elastic/security-defend-workflows\r\n\r\n*
x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts\r\n\r\n###
elastic/security-detection-rule-management\r\n\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/perform_timelines_installation.ts\r\n\r\n###
elastic/security-detections-response\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n\r\n###
elastic/security-engineering-productivity\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n*
x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts\r\n```\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"00789609ad663efffd7b3997ca773fe3ea5511e2","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:skip","v9.0.0","Team:Threat
Hunting:Investigations","v8.16.0"],"number":190238,"url":"https://github.com/elastic/kibana/pull/190238","mergeCommit":{"message":"[Threat
Hunting Investigations] Migrate all timeline routes to OpenAPI types
(#190238)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/security-team/issues/10235\r\nfixes:
https://github.com/elastic/security-team/issues/10237\r\n\r\nThis is the
final PR for migrating over all timeline-related schemas and\r\ntypes to
the new generated zod schemas from our OpenAPI specs.
(see\r\nhttps://github.com/elastic/security-team/issues/10110)\r\nOn top
of moving to the new schemas/types, this PR also cleans up usage\r\nof
now outdated types.\r\n\r\nI'm aware of the size of this PR but rest
assured, the changes are easy\r\nto review and for most teams, only a
handful of files need to be\r\nreviewed:\r\n\r\n```markdown\r\n###
elastic/security-defend-workflows\r\n\r\n*
x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts\r\n\r\n###
elastic/security-detection-rule-management\r\n\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/perform_timelines_installation.ts\r\n\r\n###
elastic/security-detections-response\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n\r\n###
elastic/security-engineering-productivity\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n*
x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts\r\n```\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"00789609ad663efffd7b3997ca773fe3ea5511e2"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/190238","number":190238,"mergeCommit":{"message":"[Threat
Hunting Investigations] Migrate all timeline routes to OpenAPI types
(#190238)\n\n## Summary\r\n\r\nfixes:
https://github.com/elastic/security-team/issues/10235\r\nfixes:
https://github.com/elastic/security-team/issues/10237\r\n\r\nThis is the
final PR for migrating over all timeline-related schemas and\r\ntypes to
the new generated zod schemas from our OpenAPI specs.
(see\r\nhttps://github.com/elastic/security-team/issues/10110)\r\nOn top
of moving to the new schemas/types, this PR also cleans up usage\r\nof
now outdated types.\r\n\r\nI'm aware of the size of this PR but rest
assured, the changes are easy\r\nto review and for most teams, only a
handful of files need to be\r\nreviewed:\r\n\r\n```markdown\r\n###
elastic/security-defend-workflows\r\n\r\n*
x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts\r\n\r\n###
elastic/security-detection-rule-management\r\n\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts\r\n*
x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/perform_timelines_installation.ts\r\n\r\n###
elastic/security-detections-response\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n\r\n###
elastic/security-engineering-productivity\r\n\r\n*
x-pack/test/security_solution_cypress/cypress/objects/timeline.ts\r\n*
x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts\r\n```\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"00789609ad663efffd7b3997ca773fe3ea5511e2"}},{"branch":"8.x","label":"v8.16.0","labelRegex":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Jan Monschke 2024-10-01 19:06:41 +02:00 committed by GitHub
parent 5f0493655d
commit 1186ed4504
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 2041 additions and 2689 deletions

View file

@ -16233,19 +16233,21 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
nullable: true
data:
type: object
properties:
getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- getOneTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: Indicates that the (template) timeline was found and returned.
summary: >-
Get an existing saved timeline or timeline template. This API is used to
@ -16284,23 +16286,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -16361,21 +16348,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates the timeline was successfully created.
'405':
content:
@ -16392,6 +16366,37 @@ paths:
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_copy:
get:
description: |
Copies and returns a timeline or timeline template.
operationId: CopyTimeline
requestBody:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
timeline:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
timelineIdToCopy:
type: string
required:
- timeline
- timelineIdToCopy
required: true
responses:
'200':
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates that the timeline has been successfully copied.
summary: Copies timeline or timeline template
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_draft:
get:
operationId: GetDraftTimelines
@ -16406,23 +16411,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates that the draft timeline was successfully retrieved.
'403':
content:
@ -16482,23 +16472,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -16651,28 +16626,14 @@ paths:
schema:
type: object
properties:
file:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_Readable'
- type: object
properties:
hapi:
type: object
properties:
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
file: {}
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- file
description: The timelines to import as a readable stream.
required: true
responses:
@ -16680,13 +16641,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
description: Indicates the import of timelines was successful.
'400':
content:
@ -16744,7 +16700,9 @@ paths:
properties:
prepackagedTimelines:
items:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
nullable: true
type: array
timelinesToInstall:
items:
@ -16767,13 +16725,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
description: Indicates the installation of prepackaged timelines was successful.
'500':
content:
@ -16811,19 +16764,16 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
nullable: true
#/components/schemas/Security_Timeline_API_ResolvedTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: The (template) timeline has been found
'400':
description: The request is missing parameters
@ -16891,36 +16841,26 @@ paths:
schema:
type: object
properties:
data:
type: object
properties:
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timelines:
items:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timeline:
items:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
type: array
totalCount:
type: number
required:
- data
- timeline
- totalCount
description: Indicates that the (template) timelines were found and returned.
'400':
content:
@ -31204,30 +31144,39 @@ components:
type: object
properties:
aggregatable:
nullable: true
type: boolean
category:
nullable: true
type: string
columnHeaderType:
nullable: true
type: string
description:
nullable: true
type: string
example:
oneOf:
- type: string
- type: number
nullable: true
type: string
id:
nullable: true
type: string
indexes:
items:
type: string
nullable: true
type: array
name:
nullable: true
type: string
placeholder:
nullable: true
type: string
searchable:
nullable: true
type: boolean
type:
nullable: true
type: string
Security_Timeline_API_DataProviderQueryMatch:
type: object
@ -31249,6 +31198,10 @@ components:
type: string
queryMatch:
$ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/Security_Timeline_API_DataProviderType'
nullable: true
Security_Timeline_API_DataProviderResult:
type: object
properties:
@ -31336,41 +31289,59 @@ components:
type: object
properties:
exists:
type: boolean
nullable: true
type: string
match_all:
nullable: true
type: string
meta:
nullable: true
type: object
properties:
alias:
nullable: true
type: string
controlledBy:
nullable: true
type: string
disabled:
nullable: true
type: boolean
field:
nullable: true
type: string
formattedValue:
nullable: true
type: string
index:
nullable: true
type: string
key:
nullable: true
type: string
negate:
nullable: true
type: boolean
params:
nullable: true
type: string
type:
nullable: true
type: string
value:
nullable: true
type: string
missing:
nullable: true
type: string
query:
nullable: true
type: string
range:
nullable: true
type: string
script:
nullable: true
type: string
Security_Timeline_API_GetNotesResult:
type: object
@ -31435,6 +31406,12 @@ components:
version:
nullable: true
type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Security_Timeline_API_Note:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BareNote'
@ -31455,6 +31432,23 @@ components:
#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody
- nullable: true
type: object
Security_Timeline_API_PersistTimelineResponse:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/Security_Timeline_API_TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
Security_Timeline_API_PinnedEvent:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent'
@ -31492,34 +31486,29 @@ components:
nullable: true
type: string
value:
nullable: true
type: string
Security_Timeline_API_Readable:
oneOf:
- nullable: true
type: string
- items:
type: string
nullable: true
type: array
Security_Timeline_API_ResolvedTimeline:
type: object
properties:
_data:
additionalProperties: true
type: object
_encoding:
alias_purpose:
$ref: >-
#/components/schemas/Security_Timeline_API_SavedObjectResolveAliasPurpose
alias_target_id:
type: string
_events:
additionalProperties: true
type: object
_eventsCount:
type: number
_maxListeners:
additionalProperties: true
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
outcome:
$ref: '#/components/schemas/Security_Timeline_API_SavedObjectResolveOutcome'
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
required:
- timeline
- outcome
Security_Timeline_API_ResponseNote:
type: object
properties:
@ -31554,6 +31543,17 @@ components:
- threat_match
- zeek
type: string
Security_Timeline_API_SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
Security_Timeline_API_SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
Security_Timeline_API_SavedTimeline:
type: object
properties:
@ -31582,12 +31582,16 @@ components:
properties:
end:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
start:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
description:
nullable: true
type: string
@ -31677,6 +31681,18 @@ components:
updatedBy:
nullable: true
type: string
Security_Timeline_API_SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
Security_Timeline_API_SerializedFilterQueryResult:
type: object
properties:
@ -31726,27 +31742,64 @@ components:
Security_Timeline_API_TimelineResponse:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- $ref: >-
#/components/schemas/Security_Timeline_API_SavedTimelineWithSavedObjectId
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent'
nullable: true
type: array
Security_Timeline_API_TimelineSavedToReturnObject:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent'
nullable: true
type: array
savedObjectId:
type: string

View file

@ -20322,19 +20322,21 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
nullable: true
data:
type: object
properties:
getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- getOneTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: Indicates that the (template) timeline was found and returned.
summary: >-
Get an existing saved timeline or timeline template. This API is used to
@ -20373,23 +20375,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -20450,21 +20437,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates the timeline was successfully created.
'405':
content:
@ -20481,6 +20455,37 @@ paths:
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_copy:
get:
description: |
Copies and returns a timeline or timeline template.
operationId: CopyTimeline
requestBody:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
timeline:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
timelineIdToCopy:
type: string
required:
- timeline
- timelineIdToCopy
required: true
responses:
'200':
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates that the timeline has been successfully copied.
summary: Copies timeline or timeline template
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_draft:
get:
operationId: GetDraftTimelines
@ -20495,23 +20500,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: Indicates that the draft timeline was successfully retrieved.
'403':
content:
@ -20571,23 +20561,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_PersistTimelineResponse
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -20740,28 +20715,14 @@ paths:
schema:
type: object
properties:
file:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_Readable'
- type: object
properties:
hapi:
type: object
properties:
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
file: {}
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- file
description: The timelines to import as a readable stream.
required: true
responses:
@ -20769,13 +20730,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
description: Indicates the import of timelines was successful.
'400':
content:
@ -20833,7 +20789,9 @@ paths:
properties:
prepackagedTimelines:
items:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
nullable: true
type: array
timelinesToInstall:
items:
@ -20856,13 +20814,8 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
description: Indicates the installation of prepackaged timelines was successful.
'500':
content:
@ -20900,19 +20853,16 @@ paths:
content:
application/json; Elastic-Api-Version=2023-10-31:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
data:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
nullable: true
#/components/schemas/Security_Timeline_API_ResolvedTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: The (template) timeline has been found
'400':
description: The request is missing parameters
@ -20980,36 +20930,26 @@ paths:
schema:
type: object
properties:
data:
type: object
properties:
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timelines:
items:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timeline:
items:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
type: array
totalCount:
type: number
required:
- data
- timeline
- totalCount
description: Indicates that the (template) timelines were found and returned.
'400':
content:
@ -39213,30 +39153,39 @@ components:
type: object
properties:
aggregatable:
nullable: true
type: boolean
category:
nullable: true
type: string
columnHeaderType:
nullable: true
type: string
description:
nullable: true
type: string
example:
oneOf:
- type: string
- type: number
nullable: true
type: string
id:
nullable: true
type: string
indexes:
items:
type: string
nullable: true
type: array
name:
nullable: true
type: string
placeholder:
nullable: true
type: string
searchable:
nullable: true
type: boolean
type:
nullable: true
type: string
Security_Timeline_API_DataProviderQueryMatch:
type: object
@ -39258,6 +39207,10 @@ components:
type: string
queryMatch:
$ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/Security_Timeline_API_DataProviderType'
nullable: true
Security_Timeline_API_DataProviderResult:
type: object
properties:
@ -39345,41 +39298,59 @@ components:
type: object
properties:
exists:
type: boolean
nullable: true
type: string
match_all:
nullable: true
type: string
meta:
nullable: true
type: object
properties:
alias:
nullable: true
type: string
controlledBy:
nullable: true
type: string
disabled:
nullable: true
type: boolean
field:
nullable: true
type: string
formattedValue:
nullable: true
type: string
index:
nullable: true
type: string
key:
nullable: true
type: string
negate:
nullable: true
type: boolean
params:
nullable: true
type: string
type:
nullable: true
type: string
value:
nullable: true
type: string
missing:
nullable: true
type: string
query:
nullable: true
type: string
range:
nullable: true
type: string
script:
nullable: true
type: string
Security_Timeline_API_GetNotesResult:
type: object
@ -39444,6 +39415,12 @@ components:
version:
nullable: true
type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Security_Timeline_API_Note:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BareNote'
@ -39464,6 +39441,23 @@ components:
#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody
- nullable: true
type: object
Security_Timeline_API_PersistTimelineResponse:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/Security_Timeline_API_TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
Security_Timeline_API_PinnedEvent:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent'
@ -39501,34 +39495,29 @@ components:
nullable: true
type: string
value:
nullable: true
type: string
Security_Timeline_API_Readable:
oneOf:
- nullable: true
type: string
- items:
type: string
nullable: true
type: array
Security_Timeline_API_ResolvedTimeline:
type: object
properties:
_data:
additionalProperties: true
type: object
_encoding:
alias_purpose:
$ref: >-
#/components/schemas/Security_Timeline_API_SavedObjectResolveAliasPurpose
alias_target_id:
type: string
_events:
additionalProperties: true
type: object
_eventsCount:
type: number
_maxListeners:
additionalProperties: true
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
outcome:
$ref: '#/components/schemas/Security_Timeline_API_SavedObjectResolveOutcome'
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
required:
- timeline
- outcome
Security_Timeline_API_ResponseNote:
type: object
properties:
@ -39563,6 +39552,17 @@ components:
- threat_match
- zeek
type: string
Security_Timeline_API_SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
Security_Timeline_API_SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
Security_Timeline_API_SavedTimeline:
type: object
properties:
@ -39591,12 +39591,16 @@ components:
properties:
end:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
start:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
description:
nullable: true
type: string
@ -39686,6 +39690,18 @@ components:
updatedBy:
nullable: true
type: string
Security_Timeline_API_SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
Security_Timeline_API_SerializedFilterQueryResult:
type: object
properties:
@ -39735,27 +39751,64 @@ components:
Security_Timeline_API_TimelineResponse:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- $ref: >-
#/components/schemas/Security_Timeline_API_SavedTimelineWithSavedObjectId
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent'
nullable: true
type: array
Security_Timeline_API_TimelineSavedToReturnObject:
allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent'
nullable: true
type: array
savedObjectId:
type: string

View file

@ -296,6 +296,10 @@ import type {
CleanDraftTimelinesRequestBodyInput,
CleanDraftTimelinesResponse,
} from './timeline/clean_draft_timelines/clean_draft_timelines_route.gen';
import type {
CopyTimelineRequestBodyInput,
CopyTimelineResponse,
} from './timeline/copy_timeline/copy_timeline_route.gen';
import type {
CreateTimelinesRequestBodyInput,
CreateTimelinesResponse,
@ -551,6 +555,23 @@ after 30 days. It also deletes other artifacts specific to the migration impleme
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Copies and returns a timeline or timeline template.
*/
async copyTimeline(props: CopyTimelineProps) {
this.log.info(`${new Date().toISOString()} Calling API CopyTimeline`);
return this.kbnClient
.request<CopyTimelineResponse>({
path: '/api/timeline/_copy',
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'GET',
body: props.body,
})
.catch(catchAxiosErrorFormatAndThrow);
}
async createAlertsIndex() {
this.log.info(`${new Date().toISOString()} Calling API CreateAlertsIndex`);
return this.kbnClient
@ -1946,6 +1967,9 @@ export interface BulkUpsertAssetCriticalityRecordsProps {
export interface CleanDraftTimelinesProps {
body: CleanDraftTimelinesRequestBodyInput;
}
export interface CopyTimelineProps {
body: CopyTimelineRequestBodyInput;
}
export interface CreateAlertsMigrationProps {
body: CreateAlertsMigrationRequestBodyInput;
}

View file

@ -16,7 +16,7 @@
import { z } from '@kbn/zod';
import { TimelineType, TimelineResponse } from '../model/components.gen';
import { TimelineType, PersistTimelineResponse } from '../model/components.gen';
export type CleanDraftTimelinesRequestBody = z.infer<typeof CleanDraftTimelinesRequestBody>;
export const CleanDraftTimelinesRequestBody = z.object({
@ -25,10 +25,4 @@ export const CleanDraftTimelinesRequestBody = z.object({
export type CleanDraftTimelinesRequestBodyInput = z.input<typeof CleanDraftTimelinesRequestBody>;
export type CleanDraftTimelinesResponse = z.infer<typeof CleanDraftTimelinesResponse>;
export const CleanDraftTimelinesResponse = z.object({
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse,
}),
}),
});
export const CleanDraftTimelinesResponse = PersistTimelineResponse;

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Draft Timeline API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_draft:
post:
@ -37,19 +30,7 @@ paths:
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
$ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
'403':
description: Indicates that the user does not have the required permissions to create a draft timeline.
content:

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Elastic Security - Timeline - Copy Timeline API
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { SavedTimeline, PersistTimelineResponse } from '../model/components.gen';
export type CopyTimelineRequestBody = z.infer<typeof CopyTimelineRequestBody>;
export const CopyTimelineRequestBody = z.object({
timeline: SavedTimeline,
timelineIdToCopy: z.string(),
});
export type CopyTimelineRequestBodyInput = z.input<typeof CopyTimelineRequestBody>;
export type CopyTimelineResponse = z.infer<typeof CopyTimelineResponse>;
export const CopyTimelineResponse = PersistTimelineResponse;

View file

@ -0,0 +1,34 @@
openapi: 3.0.0
info:
title: Elastic Security - Timeline - Copy Timeline API
version: '2023-10-31'
paths:
/api/timeline/_copy:
get:
x-labels: [serverless, ess]
x-codegen-enabled: true
operationId: CopyTimeline
summary: Copies timeline or timeline template
description: |
Copies and returns a timeline or timeline template.
tags:
- access:securitySolution
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [timeline, timelineIdToCopy]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
timelineIdToCopy:
type: string
responses:
'200':
description: Indicates that the timeline has been successfully copied.
content:
application/json:
schema:
$ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import { SavedTimelineRuntimeType } from '../model/api';
export const copyTimelineSchema = rt.type({
timeline: SavedTimelineRuntimeType,
timelineIdToCopy: rt.string,
});

View file

@ -17,29 +17,23 @@
import { z } from '@kbn/zod';
import {
SavedTimeline,
TimelineStatus,
TimelineType,
SavedTimeline,
TimelineResponse,
PersistTimelineResponse,
} from '../model/components.gen';
export type CreateTimelinesRequestBody = z.infer<typeof CreateTimelinesRequestBody>;
export const CreateTimelinesRequestBody = z.object({
timeline: SavedTimeline,
status: TimelineStatus.nullable().optional(),
timelineId: z.string().nullable().optional(),
templateTimelineId: z.string().nullable().optional(),
templateTimelineVersion: z.number().nullable().optional(),
timelineType: TimelineType.nullable().optional(),
version: z.string().nullable().optional(),
timeline: SavedTimeline,
});
export type CreateTimelinesRequestBodyInput = z.input<typeof CreateTimelinesRequestBody>;
export type CreateTimelinesResponse = z.infer<typeof CreateTimelinesResponse>;
export const CreateTimelinesResponse = z.object({
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse.optional(),
}),
}),
});
export const CreateTimelinesResponse = PersistTimelineResponse;

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-create.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline:
post:
@ -28,9 +21,10 @@ paths:
application/json:
schema:
type: object
required:
- timeline
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
status:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineStatus'
nullable: true
@ -49,26 +43,13 @@ paths:
version:
type: string
nullable: true
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
responses:
'200':
description: Indicates the timeline was successfully created.
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
$ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
'405':
description: Indicates that there was an error in the timeline creation.
content:

View file

@ -1,36 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import type { ResponseTimeline } from '../model/api';
import {
SavedTimelineRuntimeType,
TimelineStatusLiteralRt,
TimelineTypeLiteralRt,
} from '../model/api';
import { unionWithNullType } from '../../../utility_types';
export const createTimelineSchema = rt.intersection([
rt.type({
timeline: SavedTimelineRuntimeType,
}),
rt.partial({
status: unionWithNullType(TimelineStatusLiteralRt),
timelineId: unionWithNullType(rt.string),
templateTimelineId: unionWithNullType(rt.string),
templateTimelineVersion: unionWithNullType(rt.number),
timelineType: unionWithNullType(TimelineTypeLiteralRt),
version: unionWithNullType(rt.string),
}),
]);
export interface CreateTimelinesResponse {
data: {
persistTimeline: ResponseTimeline;
};
}

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Notes API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/note:
delete:

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-delete.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline:
delete:

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-import.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_export:
post:

View file

@ -16,7 +16,7 @@
import { z } from '@kbn/zod';
import { TimelineType, TimelineResponse } from '../model/components.gen';
import { TimelineType, PersistTimelineResponse } from '../model/components.gen';
export type GetDraftTimelinesRequestQuery = z.infer<typeof GetDraftTimelinesRequestQuery>;
export const GetDraftTimelinesRequestQuery = z.object({
@ -25,10 +25,4 @@ export const GetDraftTimelinesRequestQuery = z.object({
export type GetDraftTimelinesRequestQueryInput = z.input<typeof GetDraftTimelinesRequestQuery>;
export type GetDraftTimelinesResponse = z.infer<typeof GetDraftTimelinesResponse>;
export const GetDraftTimelinesResponse = z.object({
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse,
}),
}),
});
export const GetDraftTimelinesResponse = PersistTimelineResponse;

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Get Draft Timelines API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_draft:
get:
@ -30,19 +23,7 @@ paths:
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
$ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
'403':
description: If a draft timeline was not found and we attempted to create one, it indicates that the user does not have the required permissions to create a draft timeline.
content:

View file

@ -1,14 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import { TimelineTypeLiteralRt } from '../model/api';
export const getDraftTimelineSchema = rt.type({
timelineType: TimelineTypeLiteralRt,
});

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Notes API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/note:
get:

View file

@ -32,8 +32,11 @@ export const GetTimelineRequestQuery = z.object({
export type GetTimelineRequestQueryInput = z.input<typeof GetTimelineRequestQuery>;
export type GetTimelineResponse = z.infer<typeof GetTimelineResponse>;
export const GetTimelineResponse = z.object({
data: z.object({
getOneTimeline: TimelineResponse.nullable(),
export const GetTimelineResponse = z.union([
z.object({
data: z.object({
getOneTimeline: TimelineResponse,
}),
}),
});
z.object({}).strict(),
]);

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/_get_timeline_or_timeline_template_by_savedobjectid.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline:
get:
@ -38,13 +31,15 @@ paths:
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [getOneTimeline]
oneOf:
- type: object
required: [data]
properties:
getOneTimeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
nullable: true
data:
type: object
required: [getOneTimeline]
properties:
getOneTimeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
- type: object
additionalProperties: false

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
export const getTimelineQuerySchema = rt.partial({
template_timeline_id: rt.string,
id: rt.string,
});
export type GetTimelineQuery = rt.TypeOf<typeof getTimelineQuerySchema>;

View file

@ -41,13 +41,11 @@ export type GetTimelinesRequestQueryInput = z.input<typeof GetTimelinesRequestQu
export type GetTimelinesResponse = z.infer<typeof GetTimelinesResponse>;
export const GetTimelinesResponse = z.object({
data: z.object({
timelines: z.array(TimelineResponse),
totalCount: z.number(),
defaultTimelineCount: z.number(),
templateTimelineCount: z.number(),
favoriteCount: z.number(),
elasticTemplateTimelineCount: z.number(),
customTemplateTimelineCount: z.number(),
}),
timeline: z.array(TimelineResponse),
totalCount: z.number(),
defaultTimelineCount: z.number().optional(),
templateTimelineCount: z.number().optional(),
favoriteCount: z.number().optional(),
elasticTemplateTimelineCount: z.number().optional(),
customTemplateTimelineCount: z.number().optional(),
});

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-get.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timelines:
get:
@ -74,37 +67,27 @@ paths:
application/json:
schema:
type: object
required: [data]
required: [
timeline,
totalCount,
]
properties:
data:
type: object
required:
[
timelines,
totalCount,
defaultTimelineCount,
templateTimelineCount,
favoriteCount,
elasticTemplateTimelineCount,
customTemplateTimelineCount,
]
properties:
timelines:
type: array
items:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
totalCount:
type: number
defaultTimelineCount:
type: number
templateTimelineCount:
type: number
favoriteCount:
type: number
elasticTemplateTimelineCount:
type: number
customTemplateTimelineCount:
type: number
timeline:
type: array
items:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
totalCount:
type: number
defaultTimelineCount:
type: number
templateTimelineCount:
type: number
favoriteCount:
type: number
elasticTemplateTimelineCount:
type: number
customTemplateTimelineCount:
type: number
'400':
description: Bad request. The user supplied invalid data.
content:

View file

@ -1,28 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import {
direction,
sortFieldTimeline,
TimelineStatusLiteralRt,
TimelineTypeLiteralRt,
} from '../model/api';
import { unionWithNullType } from '../../../utility_types';
const BoolFromString = rt.union([rt.literal('true'), rt.literal('false')]);
export const getTimelinesQuerySchema = rt.partial({
only_user_favorite: unionWithNullType(BoolFromString),
page_index: unionWithNullType(rt.string),
page_size: unionWithNullType(rt.string),
search: unionWithNullType(rt.string),
sort_field: sortFieldTimeline,
sort_order: direction,
status: unionWithNullType(TimelineStatusLiteralRt),
timeline_type: unionWithNullType(TimelineTypeLiteralRt),
});

View file

@ -16,23 +16,14 @@
import { z } from '@kbn/zod';
import { Readable, ImportTimelineResult } from '../model/components.gen';
import { ImportTimelineResult } from '../model/components.gen';
export type ImportTimelinesRequestBody = z.infer<typeof ImportTimelinesRequestBody>;
export const ImportTimelinesRequestBody = z.object({
file: Readable.merge(
z.object({
hapi: z.object({
filename: z.string(),
headers: z.object({}),
isImmutable: z.enum(['true', 'false']).optional(),
}),
})
),
isImmutable: z.enum(['true', 'false']).optional(),
file: z.unknown(),
});
export type ImportTimelinesRequestBodyInput = z.input<typeof ImportTimelinesRequestBody>;
export type ImportTimelinesResponse = z.infer<typeof ImportTimelinesResponse>;
export const ImportTimelinesResponse = z.object({
data: ImportTimelineResult,
});
export const ImportTimelinesResponse = ImportTimelineResult;

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-import.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_import:
post:
@ -28,37 +21,21 @@ paths:
application/json:
schema:
type: object
required: [file]
properties:
file:
allOf:
- $ref: '../model/components.schema.yaml#/components/schemas/Readable'
- type: object
required: [hapi]
properties:
hapi:
type: object
required: [filename, headers]
properties:
filename:
type: string
headers:
type: object
isImmutable:
type: string
enum:
- 'true'
- 'false'
isImmutable:
type: string
enum:
- 'true'
- 'false'
file: {}
responses:
'200':
description: Indicates the import of timelines was successful.
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
$ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
$ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
'400':
description: Indicates the import of timelines was unsuccessful because of an invalid file extension.

View file

@ -1,60 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import { BareNoteSchema, SavedTimelineRuntimeType } from '../model/api';
import { unionWithNullType } from '../../../utility_types';
const pinnedEventIds = unionWithNullType(rt.array(rt.string));
export const eventNotes = unionWithNullType(rt.array(BareNoteSchema));
export const globalNotes = unionWithNullType(rt.array(BareNoteSchema));
export const ImportTimelinesSchemaRt = rt.intersection([
SavedTimelineRuntimeType,
rt.type({
savedObjectId: unionWithNullType(rt.string),
version: unionWithNullType(rt.string),
}),
rt.type({
globalNotes,
eventNotes,
pinnedEventIds,
}),
]);
export type ImportTimelinesSchema = rt.TypeOf<typeof ImportTimelinesSchemaRt>;
const ReadableRt = rt.partial({
_maxListeners: rt.unknown,
_readableState: rt.unknown,
_read: rt.unknown,
readable: rt.boolean,
_events: rt.unknown,
_eventsCount: rt.number,
_data: rt.unknown,
_position: rt.number,
_encoding: rt.string,
});
const booleanInString = rt.union([rt.literal('true'), rt.literal('false')]);
export const ImportTimelinesPayloadSchemaRt = rt.intersection([
rt.type({
file: rt.intersection([
ReadableRt,
rt.type({
hapi: rt.type({
filename: rt.string,
headers: rt.unknown,
}),
}),
]),
}),
rt.partial({ isImmutable: booleanInString }),
]);

View file

@ -7,13 +7,3 @@
export * from './model/api';
export * from './routes';
export * from './get_draft_timelines/get_draft_timelines_route';
export * from './create_timelines/create_timelines_route';
export * from './get_timeline/get_timeline_route';
export * from './get_timelines/get_timelines_route';
export * from './import_timelines/import_timelines_route';
export * from './patch_timelines/patch_timelines_schema';
export * from './pinned_events/pinned_events_route';
export * from './install_prepackaged_timelines/install_prepackaged_timelines';
export * from './copy_timeline/copy_timeline_route';

View file

@ -1,19 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import { unionWithNullType } from '../../../utility_types';
import { ImportTimelinesSchemaRt, TimelineSavedToReturnObjectRuntimeType } from '..';
export const checkTimelineStatusRt = rt.type({
timelinesToInstall: rt.array(unionWithNullType(ImportTimelinesSchemaRt)),
timelinesToUpdate: rt.array(unionWithNullType(ImportTimelinesSchemaRt)),
prepackagedTimelines: rt.array(unionWithNullType(TimelineSavedToReturnObjectRuntimeType)),
});
export type CheckTimelineStatusRt = rt.TypeOf<typeof checkTimelineStatusRt>;

View file

@ -16,7 +16,11 @@
import { z } from '@kbn/zod';
import { ImportTimelines, SavedTimeline, ImportTimelineResult } from '../model/components.gen';
import {
ImportTimelines,
TimelineSavedToReturnObject,
ImportTimelineResult,
} from '../model/components.gen';
export type InstallPrepackedTimelinesRequestBody = z.infer<
typeof InstallPrepackedTimelinesRequestBody
@ -24,13 +28,11 @@ export type InstallPrepackedTimelinesRequestBody = z.infer<
export const InstallPrepackedTimelinesRequestBody = z.object({
timelinesToInstall: z.array(ImportTimelines.nullable()),
timelinesToUpdate: z.array(ImportTimelines.nullable()),
prepackagedTimelines: z.array(SavedTimeline),
prepackagedTimelines: z.array(TimelineSavedToReturnObject.nullable()),
});
export type InstallPrepackedTimelinesRequestBodyInput = z.input<
typeof InstallPrepackedTimelinesRequestBody
>;
export type InstallPrepackedTimelinesResponse = z.infer<typeof InstallPrepackedTimelinesResponse>;
export const InstallPrepackedTimelinesResponse = z.object({
data: ImportTimelineResult,
});
export const InstallPrepackedTimelinesResponse = ImportTimelineResult;

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Install Prepackaged Timelines API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_prepackaged:
post:
@ -40,18 +33,15 @@ paths:
prepackagedTimelines:
type: array
items:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
$ref: '../model/components.schema.yaml#/components/schemas/TimelineSavedToReturnObject'
nullable: true
responses:
'200':
description: Indicates the installation of prepackaged timelines was successful.
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
$ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
$ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
'500':
description: Indicates the installation of prepackaged timelines was unsuccessful.
content:

View file

@ -5,30 +5,35 @@
* 2.0.
*/
import * as runtimeTypes from 'io-ts';
import { PositiveInteger } from '@kbn/securitysolution-io-ts-types';
import { stringEnum, unionWithNullType } from '../../../utility_types';
import type { Maybe } from '../../../search_strategy';
import { Direction } from '../../../search_strategy';
import { PinnedEventRuntimeType } from '../pinned_events/pinned_events_route';
import { ErrorSchema } from './error_schema';
import type { DataProviderType } from './components.gen';
import {
BareNote,
BarePinnedEvent,
ColumnHeaderResult,
DataProviderTypeEnum,
DataProviderResult,
FavoriteTimelineResponse,
type FavoriteTimelineResult,
FilterTimelineResult,
ImportTimelineResult,
ImportTimelines,
type Note,
PinnedEvent,
PersistTimelineResponse,
QueryMatchResult,
ResolvedTimeline,
RowRendererId,
RowRendererIdEnum,
SavedTimeline,
SavedTimelineWithSavedObjectId,
Sort,
SortDirection,
SortFieldTimeline,
SortFieldTimelineEnum,
TemplateTimelineType,
TemplateTimelineTypeEnum,
TimelineErrorResponse,
TimelineResponse,
TimelineSavedToReturnObject,
TimelineStatus,
TimelineStatusEnum,
TimelineType,
@ -38,412 +43,54 @@ import {
export {
BareNote,
BarePinnedEvent,
ColumnHeaderResult,
DataProviderResult,
DataProviderType,
DataProviderTypeEnum,
FavoriteTimelineResponse,
FilterTimelineResult,
ImportTimelineResult,
ImportTimelines,
Note,
PinnedEvent,
PersistTimelineResponse,
QueryMatchResult,
ResolvedTimeline,
RowRendererId,
RowRendererIdEnum,
SavedTimeline,
SavedTimelineWithSavedObjectId,
Sort,
SortDirection,
SortFieldTimeline,
SortFieldTimelineEnum,
TemplateTimelineType,
TimelineErrorResponse,
TimelineResponse,
TemplateTimelineTypeEnum,
TimelineSavedToReturnObject,
TimelineStatus,
TimelineStatusEnum,
TimelineType,
TimelineTypeEnum,
};
export type BarePinnedEventWithoutExternalRefs = Omit<BarePinnedEvent, 'timelineId'>;
/**
* Outcome is a property of the saved object resolve api
* will tell us info about the rule after 8.0 migrations
*/
export type SavedObjectResolveOutcome = runtimeTypes.TypeOf<typeof SavedObjectResolveOutcome>;
export const SavedObjectResolveOutcome = runtimeTypes.union([
runtimeTypes.literal('exactMatch'),
runtimeTypes.literal('aliasMatch'),
runtimeTypes.literal('conflict'),
]);
export type SavedObjectResolveAliasTargetId = runtimeTypes.TypeOf<
typeof SavedObjectResolveAliasTargetId
>;
export const SavedObjectResolveAliasTargetId = runtimeTypes.string;
export type SavedObjectResolveAliasPurpose = runtimeTypes.TypeOf<
typeof SavedObjectResolveAliasPurpose
>;
export const SavedObjectResolveAliasPurpose = runtimeTypes.union([
runtimeTypes.literal('savedObjectConversion'),
runtimeTypes.literal('savedObjectImport'),
]);
export const BareNoteSchema = runtimeTypes.intersection([
runtimeTypes.type({
timelineId: runtimeTypes.string,
}),
runtimeTypes.partial({
eventId: unionWithNullType(runtimeTypes.string),
note: unionWithNullType(runtimeTypes.string),
created: unionWithNullType(runtimeTypes.number),
createdBy: unionWithNullType(runtimeTypes.string),
updated: unionWithNullType(runtimeTypes.number),
updatedBy: unionWithNullType(runtimeTypes.string),
}),
]);
/**
* This type represents a note type stored in a saved object that does not include any fields that reference
* other saved objects.
*/
export type BareNoteWithoutExternalRefs = Omit<BareNote, 'timelineId'>;
export const NoteRuntimeType = runtimeTypes.intersection([
BareNoteSchema,
runtimeTypes.type({
noteId: runtimeTypes.string,
version: runtimeTypes.string,
}),
]);
/*
* ColumnHeader Types
*/
const SavedColumnHeaderRuntimeType = runtimeTypes.partial({
aggregatable: unionWithNullType(runtimeTypes.boolean),
category: unionWithNullType(runtimeTypes.string),
columnHeaderType: unionWithNullType(runtimeTypes.string),
description: unionWithNullType(runtimeTypes.string),
example: unionWithNullType(runtimeTypes.string),
indexes: unionWithNullType(runtimeTypes.array(runtimeTypes.string)),
id: unionWithNullType(runtimeTypes.string),
name: unionWithNullType(runtimeTypes.string),
placeholder: unionWithNullType(runtimeTypes.string),
searchable: unionWithNullType(runtimeTypes.boolean),
type: unionWithNullType(runtimeTypes.string),
});
/*
* DataProvider Types
*/
const SavedDataProviderQueryMatchBasicRuntimeType = runtimeTypes.partial({
field: unionWithNullType(runtimeTypes.string),
displayField: unionWithNullType(runtimeTypes.string),
value: runtimeTypes.union([
runtimeTypes.null,
runtimeTypes.string,
runtimeTypes.array(runtimeTypes.string),
]),
displayValue: unionWithNullType(runtimeTypes.string),
operator: unionWithNullType(runtimeTypes.string),
});
const SavedDataProviderQueryMatchRuntimeType = runtimeTypes.partial({
id: unionWithNullType(runtimeTypes.string),
name: unionWithNullType(runtimeTypes.string),
enabled: unionWithNullType(runtimeTypes.boolean),
excluded: unionWithNullType(runtimeTypes.boolean),
kqlQuery: unionWithNullType(runtimeTypes.string),
queryMatch: unionWithNullType(SavedDataProviderQueryMatchBasicRuntimeType),
});
export const DataProviderTypeLiteralRt = runtimeTypes.union([
runtimeTypes.literal(DataProviderTypeEnum.default),
runtimeTypes.literal(DataProviderTypeEnum.template),
]);
const SavedDataProviderRuntimeType = runtimeTypes.partial({
id: unionWithNullType(runtimeTypes.string),
name: unionWithNullType(runtimeTypes.string),
enabled: unionWithNullType(runtimeTypes.boolean),
excluded: unionWithNullType(runtimeTypes.boolean),
kqlQuery: unionWithNullType(runtimeTypes.string),
queryMatch: unionWithNullType(SavedDataProviderQueryMatchBasicRuntimeType),
and: unionWithNullType(runtimeTypes.array(SavedDataProviderQueryMatchRuntimeType)),
type: unionWithNullType(DataProviderTypeLiteralRt),
});
/*
* Filters Types
*/
const SavedFilterMetaRuntimeType = runtimeTypes.partial({
alias: unionWithNullType(runtimeTypes.string),
controlledBy: unionWithNullType(runtimeTypes.string),
disabled: unionWithNullType(runtimeTypes.boolean),
field: unionWithNullType(runtimeTypes.string),
formattedValue: unionWithNullType(runtimeTypes.string),
index: unionWithNullType(runtimeTypes.string),
key: unionWithNullType(runtimeTypes.string),
negate: unionWithNullType(runtimeTypes.boolean),
params: unionWithNullType(runtimeTypes.string),
type: unionWithNullType(runtimeTypes.string),
value: unionWithNullType(runtimeTypes.string),
});
const SavedFilterRuntimeType = runtimeTypes.partial({
exists: unionWithNullType(runtimeTypes.string),
meta: unionWithNullType(SavedFilterMetaRuntimeType),
match_all: unionWithNullType(runtimeTypes.string),
missing: unionWithNullType(runtimeTypes.string),
query: unionWithNullType(runtimeTypes.string),
range: unionWithNullType(runtimeTypes.string),
script: unionWithNullType(runtimeTypes.string),
});
/*
* eqlOptionsQuery -> filterQuery Types
*/
const EqlOptionsRuntimeType = runtimeTypes.partial({
eventCategoryField: unionWithNullType(runtimeTypes.string),
query: unionWithNullType(runtimeTypes.string),
tiebreakerField: unionWithNullType(runtimeTypes.string),
timestampField: unionWithNullType(runtimeTypes.string),
size: unionWithNullType(runtimeTypes.union([runtimeTypes.string, runtimeTypes.number])),
});
/*
* kqlQuery -> filterQuery Types
*/
const SavedKueryFilterQueryRuntimeType = runtimeTypes.partial({
kind: unionWithNullType(runtimeTypes.string),
expression: unionWithNullType(runtimeTypes.string),
});
const SavedSerializedFilterQueryQueryRuntimeType = runtimeTypes.partial({
kuery: unionWithNullType(SavedKueryFilterQueryRuntimeType),
serializedQuery: unionWithNullType(runtimeTypes.string),
});
const SavedFilterQueryQueryRuntimeType = runtimeTypes.partial({
filterQuery: unionWithNullType(SavedSerializedFilterQueryQueryRuntimeType),
});
/*
* DatePicker Range Types
*/
const SavedDateRangePickerRuntimeType = runtimeTypes.partial({
/* Before the change of all timestamp to ISO string the values of start and from
* attributes where a number. Specifically UNIX timestamps.
* To support old timeline's saved object we need to add the number io-ts type
*/
start: unionWithNullType(runtimeTypes.union([runtimeTypes.string, runtimeTypes.number])),
end: unionWithNullType(runtimeTypes.union([runtimeTypes.string, runtimeTypes.number])),
});
/*
* Favorite Types
*/
const SavedFavoriteRuntimeType = runtimeTypes.partial({
keySearch: unionWithNullType(runtimeTypes.string),
favoriteDate: unionWithNullType(runtimeTypes.number),
fullName: unionWithNullType(runtimeTypes.string),
userName: unionWithNullType(runtimeTypes.string),
});
/*
* Sort Types
*/
const SavedSortObject = runtimeTypes.partial({
columnId: unionWithNullType(runtimeTypes.string),
columnType: unionWithNullType(runtimeTypes.string),
sortDirection: unionWithNullType(runtimeTypes.string),
});
const SavedSortRuntimeType = runtimeTypes.union([
runtimeTypes.array(SavedSortObject),
SavedSortObject,
]);
export type Sort = runtimeTypes.TypeOf<typeof SavedSortRuntimeType>;
/*
* Timeline Statuses
*/
export const TimelineStatusLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TimelineStatusEnum.active),
runtimeTypes.literal(TimelineStatusEnum.draft),
runtimeTypes.literal(TimelineStatusEnum.immutable),
]);
export const RowRendererCount = Object.keys(RowRendererIdEnum).length;
export const RowRendererValues = Object.values(RowRendererId.Values);
const RowRendererIdRuntimeType = stringEnum(RowRendererIdEnum, 'RowRendererId');
/**
* Timeline types
*/
export const TimelineTypeLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TimelineTypeEnum.template),
runtimeTypes.literal(TimelineTypeEnum.default),
]);
/**
* This is the response type
*/
export const SavedTimelineRuntimeType = runtimeTypes.partial({
columns: unionWithNullType(runtimeTypes.array(SavedColumnHeaderRuntimeType)),
dataProviders: unionWithNullType(runtimeTypes.array(SavedDataProviderRuntimeType)),
dataViewId: unionWithNullType(runtimeTypes.string),
description: unionWithNullType(runtimeTypes.string),
eqlOptions: unionWithNullType(EqlOptionsRuntimeType),
eventType: unionWithNullType(runtimeTypes.string),
excludedRowRendererIds: unionWithNullType(runtimeTypes.array(RowRendererIdRuntimeType)),
favorite: unionWithNullType(runtimeTypes.array(SavedFavoriteRuntimeType)),
filters: unionWithNullType(runtimeTypes.array(SavedFilterRuntimeType)),
indexNames: unionWithNullType(runtimeTypes.array(runtimeTypes.string)),
kqlMode: unionWithNullType(runtimeTypes.string),
kqlQuery: unionWithNullType(SavedFilterQueryQueryRuntimeType),
title: unionWithNullType(runtimeTypes.string),
templateTimelineId: unionWithNullType(runtimeTypes.string),
templateTimelineVersion: unionWithNullType(runtimeTypes.number),
timelineType: unionWithNullType(TimelineTypeLiteralRt),
dateRange: unionWithNullType(SavedDateRangePickerRuntimeType),
savedQueryId: unionWithNullType(runtimeTypes.string),
sort: unionWithNullType(SavedSortRuntimeType),
status: unionWithNullType(TimelineStatusLiteralRt),
created: unionWithNullType(runtimeTypes.number),
createdBy: unionWithNullType(runtimeTypes.string),
updated: unionWithNullType(runtimeTypes.number),
updatedBy: unionWithNullType(runtimeTypes.string),
savedSearchId: unionWithNullType(runtimeTypes.string),
});
export type SavedTimeline = runtimeTypes.TypeOf<typeof SavedTimelineRuntimeType>;
export type SavedTimelineWithSavedObjectId = SavedTimeline & {
savedObjectId?: string | null;
};
/**
* This type represents a timeline type stored in a saved object that does not include any fields that reference
* other saved objects.
*/
export type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'savedQueryId'>;
export const TimelineSavedToReturnObjectRuntimeType = runtimeTypes.intersection([
SavedTimelineRuntimeType,
runtimeTypes.type({
savedObjectId: runtimeTypes.string,
version: runtimeTypes.string,
}),
runtimeTypes.partial({
eventIdToNoteIds: runtimeTypes.array(NoteRuntimeType),
noteIds: runtimeTypes.array(runtimeTypes.string),
notes: runtimeTypes.array(NoteRuntimeType),
pinnedEventIds: runtimeTypes.array(runtimeTypes.string),
pinnedEventsSaveObject: runtimeTypes.array(PinnedEventRuntimeType),
}),
]);
export type TimelineSavedObject = runtimeTypes.TypeOf<
typeof TimelineSavedToReturnObjectRuntimeType
>;
export const SingleTimelineResponseType = runtimeTypes.type({
data: runtimeTypes.type({
getOneTimeline: TimelineSavedToReturnObjectRuntimeType,
}),
});
export type SingleTimelineResponse = runtimeTypes.TypeOf<typeof SingleTimelineResponseType>;
/** Resolved Timeline Response */
export const ResolvedTimelineSavedObjectToReturnObjectRuntimeType = runtimeTypes.intersection([
runtimeTypes.type({
timeline: TimelineSavedToReturnObjectRuntimeType,
outcome: SavedObjectResolveOutcome,
}),
runtimeTypes.partial({
alias_target_id: SavedObjectResolveAliasTargetId,
alias_purpose: SavedObjectResolveAliasPurpose,
}),
]);
export type ResolvedTimelineWithOutcomeSavedObject = runtimeTypes.TypeOf<
typeof ResolvedTimelineSavedObjectToReturnObjectRuntimeType
>;
export const ResolvedSingleTimelineResponseType = runtimeTypes.type({
data: ResolvedTimelineSavedObjectToReturnObjectRuntimeType,
});
export type SingleTimelineResolveResponse = runtimeTypes.TypeOf<
typeof ResolvedSingleTimelineResponseType
>;
const responseTimelines = runtimeTypes.type({
timeline: runtimeTypes.array(TimelineSavedToReturnObjectRuntimeType),
totalCount: runtimeTypes.number,
});
export type ResponseTimelines = runtimeTypes.TypeOf<typeof responseTimelines>;
export const allTimelinesResponse = runtimeTypes.intersection([
responseTimelines,
runtimeTypes.type({
defaultTimelineCount: runtimeTypes.number,
templateTimelineCount: runtimeTypes.number,
elasticTemplateTimelineCount: runtimeTypes.number,
customTemplateTimelineCount: runtimeTypes.number,
favoriteCount: runtimeTypes.number,
}),
]);
export type AllTimelinesResponse = runtimeTypes.TypeOf<typeof allTimelinesResponse>;
export type BarePinnedEventWithoutExternalRefs = Omit<BarePinnedEvent, 'timelineId'>;
/**
* All Timeline Saved object type with metadata
* This type represents a note type stored in a saved object that does not include any fields that reference
* other saved objects.
*/
export const TimelineResponseType = runtimeTypes.type({
data: runtimeTypes.type({
persistTimeline: runtimeTypes.intersection([
runtimeTypes.partial({
code: unionWithNullType(runtimeTypes.number),
message: unionWithNullType(runtimeTypes.string),
}),
runtimeTypes.type({
timeline: TimelineSavedToReturnObjectRuntimeType,
}),
]),
}),
});
export type BareNoteWithoutExternalRefs = Omit<BareNote, 'timelineId'>;
export const TimelineErrorResponseType = runtimeTypes.union([
runtimeTypes.type({
status_code: runtimeTypes.number,
message: runtimeTypes.string,
}),
runtimeTypes.type({
statusCode: runtimeTypes.number,
message: runtimeTypes.string,
}),
]);
export type TimelineErrorResponse = runtimeTypes.TypeOf<typeof TimelineErrorResponseType>;
export type TimelineResponse = runtimeTypes.TypeOf<typeof TimelineResponseType>;
export const sortFieldTimeline = runtimeTypes.union([
runtimeTypes.literal(SortFieldTimelineEnum.title),
runtimeTypes.literal(SortFieldTimelineEnum.description),
runtimeTypes.literal(SortFieldTimelineEnum.updated),
runtimeTypes.literal(SortFieldTimelineEnum.created),
]);
export const direction = runtimeTypes.union([
runtimeTypes.literal(Direction.asc),
runtimeTypes.literal(Direction.desc),
]);
export const sortTimeline = runtimeTypes.type({
sortField: sortFieldTimeline,
sortOrder: direction,
});
export const RowRendererCount = Object.keys(RowRendererIdEnum).length;
export const RowRendererValues = Object.values(RowRendererId.Values);
/**
* Import/export timelines
@ -457,187 +104,17 @@ export interface ExportedNotes {
globalNotes: ExportedGlobalNotes;
}
export type ExportedTimelines = SavedTimeline &
ExportedNotes & {
pinnedEventIds: string[];
};
export interface ExportTimelineNotFoundError {
statusCode: number;
message: string;
}
export const importTimelineResultSchema = runtimeTypes.exact(
runtimeTypes.type({
success: runtimeTypes.boolean,
success_count: PositiveInteger,
timelines_installed: PositiveInteger,
timelines_updated: PositiveInteger,
errors: runtimeTypes.array(ErrorSchema),
})
);
export type ImportTimelineResultSchema = runtimeTypes.TypeOf<typeof importTimelineResultSchema>;
export const pageInfoTimeline = runtimeTypes.type({
pageIndex: runtimeTypes.number,
pageSize: runtimeTypes.number,
});
export interface PageInfoTimeline {
pageIndex: number;
pageSize: number;
}
export const getTimelinesArgs = runtimeTypes.partial({
onlyUserFavorite: unionWithNullType(runtimeTypes.boolean),
pageInfo: unionWithNullType(pageInfoTimeline),
search: unionWithNullType(runtimeTypes.string),
sort: unionWithNullType(sortTimeline),
status: unionWithNullType(TimelineStatusLiteralRt),
timelineType: unionWithNullType(TimelineTypeLiteralRt),
});
export type GetTimelinesArgs = runtimeTypes.TypeOf<typeof getTimelinesArgs>;
export interface ColumnHeaderResult {
aggregatable?: Maybe<boolean>;
category?: Maybe<string>;
columnHeaderType?: Maybe<string>;
description?: Maybe<string>;
example?: Maybe<string | number>;
indexes?: Maybe<string[]>;
id?: Maybe<string>;
name?: Maybe<string>;
placeholder?: Maybe<string>;
searchable?: Maybe<boolean>;
type?: Maybe<string>;
}
export interface DataProviderResult {
id?: Maybe<string>;
name?: Maybe<string>;
enabled?: Maybe<boolean>;
excluded?: Maybe<boolean>;
kqlQuery?: Maybe<string>;
queryMatch?: Maybe<QueryMatchResult>;
type?: Maybe<DataProviderType>;
and?: Maybe<DataProviderResult[]>;
}
export interface QueryMatchResult {
field?: Maybe<string>;
displayField?: Maybe<string>;
value?: Maybe<string | string[]>;
displayValue?: Maybe<string>;
operator?: Maybe<string>;
}
export interface DateRangePickerResult {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
start?: Maybe<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
end?: Maybe<any>;
}
export interface EqlOptionsResult {
eventCategoryField?: Maybe<string>;
tiebreakerField?: Maybe<string>;
timestampField?: Maybe<string>;
query?: Maybe<string>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
size?: Maybe<any>;
}
export interface FilterTimelineResult {
exists?: Maybe<string>;
meta?: Maybe<FilterMetaTimelineResult>;
match_all?: Maybe<string>;
missing?: Maybe<string>;
query?: Maybe<string>;
range?: Maybe<string>;
script?: Maybe<string>;
}
export interface FilterMetaTimelineResult {
alias?: Maybe<string>;
controlledBy?: Maybe<string>;
disabled?: Maybe<boolean>;
field?: Maybe<string>;
formattedValue?: Maybe<string>;
index?: Maybe<string>;
key?: Maybe<string>;
negate?: Maybe<boolean>;
params?: Maybe<string>;
type?: Maybe<string>;
value?: Maybe<string>;
}
export interface SerializedFilterQueryResult {
filterQuery?: Maybe<SerializedKueryQueryResult>;
}
export interface KueryFilterQueryResult {
kind?: Maybe<string>;
expression?: Maybe<string>;
}
export interface SerializedKueryQueryResult {
kuery?: Maybe<KueryFilterQueryResult>;
serializedQuery?: Maybe<string>;
}
export interface TimelineResult {
columns?: Maybe<ColumnHeaderResult[]>;
created?: Maybe<number>;
createdBy?: Maybe<string>;
dataProviders?: Maybe<DataProviderResult[]>;
dataViewId?: Maybe<string>;
dateRange?: Maybe<DateRangePickerResult>;
description?: Maybe<string>;
eqlOptions?: Maybe<EqlOptionsResult>;
eventIdToNoteIds?: Maybe<Note[]>;
eventType?: Maybe<string>;
excludedRowRendererIds?: Maybe<RowRendererId[]>;
favorite?: Maybe<FavoriteTimelineResult[]>;
filters?: Maybe<FilterTimelineResult[]>;
kqlMode?: Maybe<string>;
kqlQuery?: Maybe<SerializedFilterQueryResult>;
indexNames?: Maybe<string[]>;
notes?: Maybe<Note[]>;
noteIds?: Maybe<string[]>;
pinnedEventIds?: Maybe<string[]>;
pinnedEventsSaveObject?: Maybe<PinnedEvent[]>;
savedQueryId?: Maybe<string>;
savedObjectId: string;
sort?: Maybe<Sort>;
status?: Maybe<TimelineStatus>;
title?: Maybe<string>;
templateTimelineId?: Maybe<string>;
templateTimelineVersion?: Maybe<number>;
timelineType?: Maybe<TimelineType>;
updated?: Maybe<number>;
updatedBy?: Maybe<string>;
version: string;
savedSearchId?: Maybe<string>;
}
export interface ResponseTimeline {
code?: Maybe<number>;
message?: Maybe<string>;
timeline: TimelineResult;
}
export interface SortTimeline {
sortField: SortFieldTimeline;
sortOrder: Direction;
}
export interface GetAllTimelineVariables {
pageInfo: PageInfoTimeline;
search?: Maybe<string>;
sort?: Maybe<SortTimeline>;
onlyUserFavorite?: Maybe<boolean>;
timelineType?: Maybe<TimelineType>;
status?: Maybe<TimelineStatus>;
sortOrder: SortDirection;
}

View file

@ -42,24 +42,24 @@ export const TemplateTimelineTypeEnum = TemplateTimelineType.enum;
export type ColumnHeaderResult = z.infer<typeof ColumnHeaderResult>;
export const ColumnHeaderResult = z.object({
aggregatable: z.boolean().optional(),
category: z.string().optional(),
columnHeaderType: z.string().optional(),
description: z.string().optional(),
example: z.union([z.string(), z.number()]).optional(),
indexes: z.array(z.string()).optional(),
id: z.string().optional(),
name: z.string().optional(),
placeholder: z.string().optional(),
searchable: z.boolean().optional(),
type: z.string().optional(),
aggregatable: z.boolean().nullable().optional(),
category: z.string().nullable().optional(),
columnHeaderType: z.string().nullable().optional(),
description: z.string().nullable().optional(),
example: z.string().nullable().optional(),
indexes: z.array(z.string()).nullable().optional(),
id: z.string().nullable().optional(),
name: z.string().nullable().optional(),
placeholder: z.string().nullable().optional(),
searchable: z.boolean().nullable().optional(),
type: z.string().nullable().optional(),
});
export type QueryMatchResult = z.infer<typeof QueryMatchResult>;
export const QueryMatchResult = z.object({
field: z.string().nullable().optional(),
displayField: z.string().nullable().optional(),
value: z.string().nullable().optional(),
value: z.union([z.string().nullable(), z.array(z.string()).nullable()]).optional(),
displayValue: z.string().nullable().optional(),
operator: z.string().nullable().optional(),
});
@ -71,7 +71,8 @@ export const DataProviderQueryMatch = z.object({
id: z.string().nullable().optional(),
kqlQuery: z.string().nullable().optional(),
name: z.string().nullable().optional(),
queryMatch: QueryMatchResult.optional(),
queryMatch: QueryMatchResult.nullable().optional(),
type: DataProviderType.nullable().optional(),
});
export type DataProviderResult = z.infer<typeof DataProviderResult>;
@ -119,27 +120,28 @@ export const FavoriteTimelineResult = z.object({
export type FilterTimelineResult = z.infer<typeof FilterTimelineResult>;
export const FilterTimelineResult = z.object({
exists: z.boolean().optional(),
exists: z.string().nullable().optional(),
meta: z
.object({
alias: z.string().optional(),
controlledBy: z.string().optional(),
disabled: z.boolean().optional(),
field: z.string().optional(),
formattedValue: z.string().optional(),
index: z.string().optional(),
key: z.string().optional(),
negate: z.boolean().optional(),
params: z.string().optional(),
type: z.string().optional(),
value: z.string().optional(),
alias: z.string().nullable().optional(),
controlledBy: z.string().nullable().optional(),
disabled: z.boolean().nullable().optional(),
field: z.string().nullable().optional(),
formattedValue: z.string().nullable().optional(),
index: z.string().nullable().optional(),
key: z.string().nullable().optional(),
negate: z.boolean().nullable().optional(),
params: z.string().nullable().optional(),
type: z.string().nullable().optional(),
value: z.string().nullable().optional(),
})
.nullable()
.optional(),
match_all: z.string().optional(),
missing: z.string().optional(),
query: z.string().optional(),
range: z.string().optional(),
script: z.string().optional(),
match_all: z.string().nullable().optional(),
missing: z.string().nullable().optional(),
query: z.string().nullable().optional(),
range: z.string().nullable().optional(),
script: z.string().nullable().optional(),
});
export type SerializedFilterQueryResult = z.infer<typeof SerializedFilterQueryResult>;
@ -178,8 +180,8 @@ export const SavedTimeline = z.object({
dataViewId: z.string().nullable().optional(),
dateRange: z
.object({
end: z.union([z.string(), z.number()]).optional(),
start: z.union([z.string(), z.number()]).optional(),
end: z.union([z.string().nullable(), z.number().nullable()]).optional(),
start: z.union([z.string().nullable(), z.number().nullable()]).optional(),
})
.nullable()
.optional(),
@ -213,6 +215,14 @@ export const SavedTimeline = z.object({
updatedBy: z.string().nullable().optional(),
});
export type SavedTimelineWithSavedObjectId = z.infer<typeof SavedTimelineWithSavedObjectId>;
export const SavedTimelineWithSavedObjectId = SavedTimeline.merge(
z.object({
savedObjectId: z.string(),
version: z.string(),
})
);
export type BareNote = z.infer<typeof BareNote>;
export const BareNote = z.object({
eventId: z.string().nullable().optional(),
@ -251,18 +261,50 @@ export const PinnedEvent = BarePinnedEvent.merge(
);
export type TimelineResponse = z.infer<typeof TimelineResponse>;
export const TimelineResponse = SavedTimeline.merge(
export const TimelineResponse = SavedTimeline.merge(SavedTimelineWithSavedObjectId).merge(
z.object({
eventIdToNoteIds: z.array(Note).optional(),
notes: z.array(Note).optional(),
noteIds: z.array(z.string()).optional(),
pinnedEventIds: z.array(z.string()).optional(),
pinnedEventsSaveObject: z.array(PinnedEvent).optional(),
savedObjectId: z.string(),
version: z.string(),
eventIdToNoteIds: z.array(Note).nullable().optional(),
notes: z.array(Note).nullable().optional(),
noteIds: z.array(z.string()).nullable().optional(),
pinnedEventIds: z.array(z.string()).nullable().optional(),
pinnedEventsSaveObject: z.array(PinnedEvent).nullable().optional(),
})
);
export type TimelineSavedToReturnObject = z.infer<typeof TimelineSavedToReturnObject>;
export const TimelineSavedToReturnObject = SavedTimeline.merge(
z.object({
savedObjectId: z.string(),
version: z.string(),
eventIdToNoteIds: z.array(Note).nullable().optional(),
notes: z.array(Note).nullable().optional(),
noteIds: z.array(z.string()).nullable().optional(),
pinnedEventIds: z.array(z.string()).nullable().optional(),
pinnedEventsSaveObject: z.array(PinnedEvent).nullable().optional(),
})
);
export type SavedObjectResolveOutcome = z.infer<typeof SavedObjectResolveOutcome>;
export const SavedObjectResolveOutcome = z.enum(['exactMatch', 'aliasMatch', 'conflict']);
export type SavedObjectResolveOutcomeEnum = typeof SavedObjectResolveOutcome.enum;
export const SavedObjectResolveOutcomeEnum = SavedObjectResolveOutcome.enum;
export type SavedObjectResolveAliasPurpose = z.infer<typeof SavedObjectResolveAliasPurpose>;
export const SavedObjectResolveAliasPurpose = z.enum([
'savedObjectConversion',
'savedObjectImport',
]);
export type SavedObjectResolveAliasPurposeEnum = typeof SavedObjectResolveAliasPurpose.enum;
export const SavedObjectResolveAliasPurposeEnum = SavedObjectResolveAliasPurpose.enum;
export type ResolvedTimeline = z.infer<typeof ResolvedTimeline>;
export const ResolvedTimeline = z.object({
timeline: TimelineSavedToReturnObject,
outcome: SavedObjectResolveOutcome,
alias_target_id: z.string().optional(),
alias_purpose: SavedObjectResolveAliasPurpose.optional(),
});
export type FavoriteTimelineResponse = z.infer<typeof FavoriteTimelineResponse>;
export const FavoriteTimelineResponse = z.object({
savedObjectId: z.string(),
@ -275,6 +317,15 @@ export const FavoriteTimelineResponse = z.object({
favorite: z.array(FavoriteTimelineResult).optional(),
});
export type PersistTimelineResponse = z.infer<typeof PersistTimelineResponse>;
export const PersistTimelineResponse = z.object({
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse,
}),
}),
});
export type BareNoteWithoutExternalRefs = z.infer<typeof BareNoteWithoutExternalRefs>;
export const BareNoteWithoutExternalRefs = z.object({
eventId: z.string().nullable().optional(),
@ -306,6 +357,11 @@ export const SortFieldTimeline = z.enum(['title', 'description', 'updated', 'cre
export type SortFieldTimelineEnum = typeof SortFieldTimeline.enum;
export const SortFieldTimelineEnum = SortFieldTimeline.enum;
export type SortDirection = z.infer<typeof SortDirection>;
export const SortDirection = z.enum(['asc', 'desc']);
export type SortDirectionEnum = typeof SortDirection.enum;
export const SortDirectionEnum = SortDirection.enum;
/**
* The status of the timeline. Valid values are `active`, `draft`, and `immutable`.
*/
@ -317,11 +373,11 @@ export const TimelineStatusEnum = TimelineStatus.enum;
export type ImportTimelines = z.infer<typeof ImportTimelines>;
export const ImportTimelines = SavedTimeline.merge(
z.object({
savedObjectId: z.string().nullable().optional(),
version: z.string().nullable().optional(),
globalNotes: z.array(BareNote).nullable().optional(),
eventNotes: z.array(BareNote).nullable().optional(),
pinnedEventIds: z.array(z.string()).nullable().optional(),
savedObjectId: z.string().nullable(),
version: z.string().nullable(),
pinnedEventIds: z.array(z.string()).nullable(),
eventNotes: z.array(BareNote).nullable(),
globalNotes: z.array(BareNote).nullable(),
})
);
@ -346,24 +402,14 @@ export const ImportTimelineResult = z.object({
.optional(),
});
export type ExportedTimelines = z.infer<typeof ExportedTimelines>;
export const ExportedTimelines = SavedTimeline.merge(
export type TimelineErrorResponse = z.infer<typeof TimelineErrorResponse>;
export const TimelineErrorResponse = z.union([
z.object({
globalNotes: z.array(Note).optional(),
eventNotes: z.array(Note).optional(),
pinnedEventIds: z.array(z.string()).optional(),
})
);
export type Readable = z.infer<typeof Readable>;
export const Readable = z.object({
_maxListeners: z.object({}).catchall(z.unknown()).optional(),
_readableState: z.object({}).catchall(z.unknown()).optional(),
_read: z.object({}).catchall(z.unknown()).optional(),
readable: z.boolean().optional(),
_events: z.object({}).catchall(z.unknown()).optional(),
_eventsCount: z.number().optional(),
_data: z.object({}).catchall(z.unknown()).optional(),
_position: z.number().optional(),
_encoding: z.string().optional(),
});
message: z.string(),
status_code: z.number(),
}),
z.object({
message: z.string(),
statusCode: z.number(),
}),
]);

View file

@ -56,11 +56,15 @@ components:
end:
oneOf:
- type: string
nullable: true
- type: number
nullable: true
start:
oneOf:
- type: string
nullable: true
- type: number
nullable: true
description:
type: string
nullable: true
@ -149,43 +153,73 @@ components:
updatedBy:
type: string
nullable: true
TimelineResponse:
SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
required:
- savedObjectId
- version
required: [savedObjectId, version]
properties:
eventIdToNoteIds:
type: array
items:
$ref: '#/components/schemas/Note'
notes:
type: array
items:
$ref: '#/components/schemas/Note'
noteIds:
type: array
items:
type: string
pinnedEventIds:
type: array
items:
type: string
pinnedEventsSaveObject:
type: array
items:
$ref: '#/components/schemas/PinnedEvent'
savedObjectId:
type: string
version:
type: string
TimelineResponse:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- $ref: '#/components/schemas/SavedTimelineWithSavedObjectId'
- type: object
properties:
eventIdToNoteIds:
type: array
nullable: true
items:
$ref: '#/components/schemas/Note'
notes:
type: array
nullable: true
items:
$ref: '#/components/schemas/Note'
noteIds:
type: array
nullable: true
items:
type: string
pinnedEventIds:
type: array
nullable: true
items:
type: string
pinnedEventsSaveObject:
type: array
nullable: true
items:
$ref: '#/components/schemas/PinnedEvent'
ResolvedTimeline:
type: object
required: [timeline, outcome]
properties:
timeline:
$ref: '#/components/schemas/TimelineSavedToReturnObject'
outcome:
$ref: '#/components/schemas/SavedObjectResolveOutcome'
alias_target_id:
type: string
alias_purpose:
$ref: '#/components/schemas/SavedObjectResolveAliasPurpose'
SavedObjectResolveOutcome:
type: string
enum:
- exactMatch
- aliasMatch
- conflict
SavedObjectResolveAliasPurpose:
type: string
enum:
- savedObjectConversion
- savedObjectImport
FavoriteTimelineResponse:
type: object
required:
- savedObjectId
- version
required: [savedObjectId, version]
properties:
savedObjectId:
type: string
@ -209,35 +243,58 @@ components:
type: array
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'
ColumnHeaderResult:
type: object
properties:
aggregatable:
type: boolean
nullable: true
category:
type: string
nullable: true
columnHeaderType:
type: string
nullable: true
description:
type: string
nullable: true
example:
oneOf:
- type: string
- type: number
type: string
nullable: true
indexes:
type: array
nullable: true
items:
type: string
id:
type: string
nullable: true
name:
type: string
nullable: true
placeholder:
type: string
nullable: true
searchable:
type: boolean
nullable: true
type:
type: string
nullable: true
QueryMatchResult:
type: object
properties:
@ -248,8 +305,13 @@ components:
type: string
nullable: true
value:
type: string
nullable: true
oneOf:
- type: string
nullable: true
- type: array
nullable: true
items:
type: string
displayValue:
type: string
nullable: true
@ -305,6 +367,10 @@ components:
nullable: true
queryMatch:
$ref: '#/components/schemas/QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/DataProviderType'
nullable: true
BareNoteWithoutExternalRefs:
type: object
properties:
@ -419,42 +485,60 @@ components:
type: object
properties:
exists:
type: boolean
type: string
nullable: true
meta:
type: object
nullable: true
properties:
alias:
type: string
nullable: true
controlledBy:
type: string
nullable: true
disabled:
type: boolean
nullable: true
field:
type: string
nullable: true
formattedValue:
type: string
nullable: true
index:
type: string
nullable: true
key:
type: string
nullable: true
negate:
type: boolean
nullable: true
params:
type: string
nullable: true
type:
type: string
nullable: true
value:
type: string
nullable: true
match_all:
type: string
nullable: true
missing:
type: string
nullable: true
query:
type: string
nullable: true
range:
type: string
nullable: true
script:
type: string
nullable: true
SerializedFilterQueryResult:
type: object
properties:
@ -531,6 +615,11 @@ components:
- description
- updated
- created
SortDirection:
type: string
enum:
- asc
- desc
TimelineStatus:
type: string
enum:
@ -544,6 +633,7 @@ components:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
required: [savedObjectId, version, pinnedEventIds, eventNotes, globalNotes]
properties:
savedObjectId:
type: string
@ -551,21 +641,56 @@ components:
version:
type: string
nullable: true
globalNotes:
nullable: true
type: array
items:
$ref: '#/components/schemas/BareNote'
eventNotes:
nullable: true
type: array
items:
$ref: '#/components/schemas/BareNote'
pinnedEventIds:
nullable: true
type: array
nullable: true
items:
type: string
eventNotes:
type: array
nullable: true
items:
$ref: '#/components/schemas/BareNote'
globalNotes:
type: array
nullable: true
items:
$ref: '#/components/schemas/BareNote'
TimelineSavedToReturnObject:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
required: [savedObjectId, version]
properties:
savedObjectId:
type: string
version:
type: string
eventIdToNoteIds:
type: array
nullable: true
items:
$ref: '#/components/schemas/Note'
notes:
type: array
nullable: true
items:
$ref: '#/components/schemas/Note'
noteIds:
type: array
nullable: true
items:
type: string
pinnedEventIds:
type: array
nullable: true
items:
type: string
pinnedEventsSaveObject:
type: array
nullable: true
items:
$ref: '#/components/schemas/PinnedEvent'
ImportTimelineResult:
type: object
properties:
@ -591,46 +716,19 @@ components:
type: string
status_code:
type: number
ExportedTimelines:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
TimelineErrorResponse:
oneOf:
- type: object
required: [message, status_code]
properties:
globalNotes:
type: array
items:
$ref: '#/components/schemas/Note'
eventNotes:
type: array
items:
$ref: '#/components/schemas/Note'
pinnedEventIds:
type: array
items:
type: string
Readable:
type: object
properties:
_maxListeners:
type: object
additionalProperties: true
_readableState:
type: object
additionalProperties: true
_read:
type: object
additionalProperties: true
readable:
type: boolean
_events:
type: object
additionalProperties: true
_eventsCount:
type: number
_data:
type: object
additionalProperties: true
_position:
type: number
_encoding:
type: string
message:
type: string
status_code:
type: number
- type: object
required: [message, statusCode]
properties:
message:
type: string
statusCode:
type: number

View file

@ -16,7 +16,7 @@
import { z } from '@kbn/zod';
import { SavedTimeline, TimelineResponse } from '../model/components.gen';
import { SavedTimeline, PersistTimelineResponse } from '../model/components.gen';
export type PatchTimelineRequestBody = z.infer<typeof PatchTimelineRequestBody>;
export const PatchTimelineRequestBody = z.object({
@ -27,10 +27,4 @@ export const PatchTimelineRequestBody = z.object({
export type PatchTimelineRequestBodyInput = z.input<typeof PatchTimelineRequestBody>;
export type PatchTimelineResponse = z.infer<typeof PatchTimelineResponse>;
export const PatchTimelineResponse = z.object({
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse,
}),
}),
});
export const PatchTimelineResponse = PersistTimelineResponse;

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Patch Timeline API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline:
patch:
@ -42,19 +35,7 @@ paths:
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
$ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
'405':
description: Indicates that the user does not have the required access to create a draft timeline.
content:

View file

@ -1,24 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as rt from 'io-ts';
import type { ResponseTimeline } from '../model/api';
import { SavedTimelineRuntimeType } from '../model/api';
import { unionWithNullType } from '../../../utility_types';
export const patchTimelineSchema = rt.type({
timeline: SavedTimelineRuntimeType,
timelineId: unionWithNullType(rt.string),
version: unionWithNullType(rt.string),
});
export interface PatchTimelinesResponse {
data: {
persistTimeline: ResponseTimeline;
};
}

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Favorite API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/_favorite:
patch:

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-update.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/note:
patch:

View file

@ -5,13 +5,6 @@ info:
externalDocs:
url: https://www.elastic.co/guide/en/security/current/_pin_an_event_to_an_existing_timeline.html
description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/pinned_event:
patch:

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as runtimeTypes from 'io-ts';
import { unionWithNullType } from '../../../utility_types';
/*
* Pinned Event Types
* TODO: remove these when the timeline types are moved to zod
*/
const BarePinnedEventType = runtimeTypes.intersection([
runtimeTypes.type({
timelineId: runtimeTypes.string,
eventId: runtimeTypes.string,
}),
runtimeTypes.partial({
created: unionWithNullType(runtimeTypes.number),
createdBy: unionWithNullType(runtimeTypes.string),
updated: unionWithNullType(runtimeTypes.number),
updatedBy: unionWithNullType(runtimeTypes.string),
}),
]);
export const PinnedEventRuntimeType = runtimeTypes.intersection([
runtimeTypes.type({
pinnedEventId: runtimeTypes.string,
version: runtimeTypes.string,
}),
BarePinnedEventType,
]);

View file

@ -16,7 +16,7 @@
import { z } from '@kbn/zod';
import { TimelineResponse } from '../model/components.gen';
import { ResolvedTimeline } from '../model/components.gen';
export type ResolveTimelineRequestQuery = z.infer<typeof ResolveTimelineRequestQuery>;
export const ResolveTimelineRequestQuery = z.object({
@ -32,8 +32,9 @@ export const ResolveTimelineRequestQuery = z.object({
export type ResolveTimelineRequestQueryInput = z.input<typeof ResolveTimelineRequestQuery>;
export type ResolveTimelineResponse = z.infer<typeof ResolveTimelineResponse>;
export const ResolveTimelineResponse = z.object({
data: z.object({
getOneTimeline: TimelineResponse.nullable(),
export const ResolveTimelineResponse = z.union([
z.object({
data: ResolvedTimeline,
}),
});
z.object({}).strict(),
]);

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info:
title: Elastic Security - Timeline - Resolve Timeline API
version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths:
/api/timeline/resolve:
get:
@ -35,16 +28,15 @@ paths:
content:
application/json:
schema:
type: object
required: [data]
properties:
data:
type: object
required: [getOneTimeline]
oneOf:
- type: object
required: [data]
properties:
getOneTimeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
nullable: true
data:
$ref: '../model/components.schema.yaml#/components/schemas/ResolvedTimeline'
- type: object
additionalProperties: false
'400':
description: The request is missing parameters
'404':

View file

@ -17,7 +17,10 @@ export {
} from './persist_note/persist_note_route.gen';
export { DeleteNoteRequestBody, DeleteNoteResponse } from './delete_note/delete_note_route.gen';
export { CleanDraftTimelinesRequestBody } from './clean_draft_timelines/clean_draft_timelines_route.gen';
export {
CleanDraftTimelinesResponse,
CleanDraftTimelinesRequestBody,
} from './clean_draft_timelines/clean_draft_timelines_route.gen';
export {
ExportTimelinesRequestQuery,
@ -40,3 +43,48 @@ export {
GetNotesResponse,
GetNotesResult,
} from './get_notes/get_notes_route.gen';
export {
CopyTimelineRequestBody,
CopyTimelineResponse,
} from './copy_timeline/copy_timeline_route.gen';
export {
CreateTimelinesRequestBody,
CreateTimelinesResponse,
} from './create_timelines/create_timelines_route.gen';
export {
PatchTimelineRequestBody,
PatchTimelineResponse,
} from './patch_timelines/patch_timeline_route.gen';
export {
ImportTimelinesRequestBody,
ImportTimelinesResponse,
} from './import_timelines/import_timelines_route.gen';
export {
InstallPrepackedTimelinesRequestBody,
InstallPrepackedTimelinesResponse,
} from './install_prepackaged_timelines/install_prepackaged_timelines_route.gen';
export {
GetDraftTimelinesRequestQuery,
GetDraftTimelinesResponse,
} from './get_draft_timelines/get_draft_timelines_route.gen';
export {
ResolveTimelineRequestQuery,
ResolveTimelineResponse,
} from './resolve_timeline/resolve_timeline_route.gen';
export {
GetTimelineRequestQuery,
GetTimelineResponse,
} from './get_timeline/get_timeline_route.gen';
export {
GetTimelinesRequestQuery,
GetTimelinesResponse,
} from './get_timelines/get_timelines_route.gen';

View file

@ -5,13 +5,7 @@
* 2.0.
*/
import type { SortField, Maybe } from '../common';
import type {
DataProviderType,
TimelineType,
TimelineStatus,
RowRendererId,
} from '../../api/timeline';
import type { SortField } from '../common';
export * from './events';
@ -20,122 +14,6 @@ export interface TimelineRequestSortField<Field = string> extends SortField<Fiel
esTypes: string[];
}
export interface ColumnHeaderInput {
aggregatable?: Maybe<boolean>;
category?: Maybe<string>;
columnHeaderType?: Maybe<string>;
description?: Maybe<string>;
example?: Maybe<string>;
indexes?: Maybe<string[]>;
id?: Maybe<string>;
name?: Maybe<string>;
placeholder?: Maybe<string>;
searchable?: Maybe<boolean>;
type?: Maybe<string>;
}
export interface QueryMatchInput {
field?: Maybe<string>;
displayField?: Maybe<string>;
value?: Maybe<string>;
displayValue?: Maybe<string>;
operator?: Maybe<string>;
}
export interface DataProviderInput {
id?: Maybe<string>;
name?: Maybe<string>;
enabled?: Maybe<boolean>;
excluded?: Maybe<boolean>;
kqlQuery?: Maybe<string>;
queryMatch?: Maybe<QueryMatchInput>;
and?: Maybe<DataProviderInput[]>;
type?: Maybe<DataProviderType>;
}
export interface EqlOptionsInput {
eventCategoryField?: Maybe<string>;
tiebreakerField?: Maybe<string>;
timestampField?: Maybe<string>;
query?: Maybe<string>;
size?: Maybe<number>;
}
export interface FilterMetaTimelineInput {
alias?: Maybe<string>;
controlledBy?: Maybe<string>;
disabled?: Maybe<boolean>;
field?: Maybe<string>;
formattedValue?: Maybe<string>;
index?: Maybe<string>;
key?: Maybe<string>;
negate?: Maybe<boolean>;
params?: Maybe<string>;
type?: Maybe<string>;
value?: Maybe<string>;
}
export interface FilterTimelineInput {
exists?: Maybe<string>;
meta?: Maybe<FilterMetaTimelineInput>;
match_all?: Maybe<string>;
missing?: Maybe<string>;
query?: Maybe<string>;
range?: Maybe<string>;
script?: Maybe<string>;
}
export interface SerializedFilterQueryInput {
filterQuery?: Maybe<SerializedKueryQueryInput>;
}
export interface SerializedKueryQueryInput {
kuery?: Maybe<KueryFilterQueryInput>;
serializedQuery?: Maybe<string>;
}
export interface KueryFilterQueryInput {
kind?: Maybe<string>;
expression?: Maybe<string>;
}
export interface DateRangePickerInput {
start?: Maybe<number>;
end?: Maybe<number>;
}
export interface SortTimelineInput {
columnId?: Maybe<string>;
sortDirection?: Maybe<string>;
}
export interface TimelineInput {
columns?: Maybe<ColumnHeaderInput[]>;
dataProviders?: Maybe<DataProviderInput[]>;
dataViewId?: Maybe<string>;
description?: Maybe<string>;
eqlOptions?: Maybe<EqlOptionsInput>;
eventType?: Maybe<string>;
excludedRowRendererIds?: Maybe<RowRendererId[]>;
filters?: Maybe<FilterTimelineInput[]>;
kqlMode?: Maybe<string>;
kqlQuery?: Maybe<SerializedFilterQueryInput>;
indexNames?: Maybe<string[]>;
title?: Maybe<string>;
templateTimelineId?: Maybe<string>;
templateTimelineVersion?: Maybe<number>;
timelineType?: Maybe<TimelineType>;
dateRange?: Maybe<DateRangePickerInput>;
savedQueryId?: Maybe<string>;
sort?: Maybe<SortTimelineInput[]>;
status?: Maybe<TimelineStatus>;
savedSearchId: Maybe<string>;
}
export enum FlowDirection {
uniDirectional = 'uniDirectional',
biDirectional = 'biDirectional',

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { ZodError, ZodType } from '@kbn/zod';
import { stringifyZodError } from '@kbn/zod-helpers';
import { type Either, fold, left, right } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
type ErrorFactory = (message: string) => Error;
const throwErrors = (createError: ErrorFactory) => (errors: ZodError) => {
throw createError(stringifyZodError(errors));
};
const parseRuntimeType =
<T>(zodType: ZodType<T>) =>
(v: unknown): Either<ZodError<T>, T> => {
const result = zodType.safeParse(v);
return result.success ? right(result.data) : left(result.error);
};
export const parseOrThrowErrorFactory =
(createError: ErrorFactory) => (runtimeType: ZodType) => (inputValue: unknown) =>
pipe(parseRuntimeType(runtimeType)(inputValue), fold(throwErrors(createError), identity));

View file

@ -263,18 +263,20 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
nullable: true
data:
type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- getOneTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: Indicates that the (template) timeline was found and returned.
summary: >-
Get an existing saved timeline or timeline template. This API is used to
@ -313,22 +315,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -389,20 +376,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates the timeline was successfully created.
'405':
content:
@ -419,6 +393,36 @@ paths:
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_copy:
get:
description: |
Copies and returns a timeline or timeline template.
operationId: CopyTimeline
requestBody:
content:
application/json:
schema:
type: object
properties:
timeline:
$ref: '#/components/schemas/SavedTimeline'
timelineIdToCopy:
type: string
required:
- timeline
- timelineIdToCopy
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates that the timeline has been successfully copied.
summary: Copies timeline or timeline template
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_draft:
get:
operationId: GetDraftTimelines
@ -433,22 +437,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates that the draft timeline was successfully retrieved.
'403':
content:
@ -508,22 +497,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -675,28 +649,14 @@ paths:
schema:
type: object
properties:
file:
allOf:
- $ref: '#/components/schemas/Readable'
- type: object
properties:
hapi:
type: object
properties:
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
file: {}
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- file
description: The timelines to import as a readable stream.
required: true
responses:
@ -704,12 +664,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
$ref: '#/components/schemas/ImportTimelineResult'
description: Indicates the import of timelines was successful.
'400':
content:
@ -767,7 +722,8 @@ paths:
properties:
prepackagedTimelines:
items:
$ref: '#/components/schemas/SavedTimeline'
$ref: '#/components/schemas/TimelineSavedToReturnObject'
nullable: true
type: array
timelinesToInstall:
items:
@ -790,12 +746,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
$ref: '#/components/schemas/ImportTimelineResult'
description: Indicates the installation of prepackaged timelines was successful.
'500':
content:
@ -833,18 +784,15 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
nullable: true
data:
$ref: '#/components/schemas/ResolvedTimeline'
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: The (template) timeline has been found
'400':
description: The request is missing parameters
@ -912,35 +860,25 @@ paths:
schema:
type: object
properties:
data:
type: object
properties:
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timelines:
items:
$ref: '#/components/schemas/TimelineResponse'
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timeline:
items:
$ref: '#/components/schemas/TimelineResponse'
type: array
totalCount:
type: number
required:
- data
- timeline
- totalCount
description: Indicates that the (template) timelines were found and returned.
'400':
content:
@ -1012,30 +950,39 @@ components:
type: object
properties:
aggregatable:
nullable: true
type: boolean
category:
nullable: true
type: string
columnHeaderType:
nullable: true
type: string
description:
nullable: true
type: string
example:
oneOf:
- type: string
- type: number
nullable: true
type: string
id:
nullable: true
type: string
indexes:
items:
type: string
nullable: true
type: array
name:
nullable: true
type: string
placeholder:
nullable: true
type: string
searchable:
nullable: true
type: boolean
type:
nullable: true
type: string
DataProviderQueryMatch:
type: object
@ -1057,6 +1004,10 @@ components:
type: string
queryMatch:
$ref: '#/components/schemas/QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/DataProviderType'
nullable: true
DataProviderResult:
type: object
properties:
@ -1144,41 +1095,59 @@ components:
type: object
properties:
exists:
type: boolean
nullable: true
type: string
match_all:
nullable: true
type: string
meta:
nullable: true
type: object
properties:
alias:
nullable: true
type: string
controlledBy:
nullable: true
type: string
disabled:
nullable: true
type: boolean
field:
nullable: true
type: string
formattedValue:
nullable: true
type: string
index:
nullable: true
type: string
key:
nullable: true
type: string
negate:
nullable: true
type: boolean
params:
nullable: true
type: string
type:
nullable: true
type: string
value:
nullable: true
type: string
missing:
nullable: true
type: string
query:
nullable: true
type: string
range:
nullable: true
type: string
script:
nullable: true
type: string
GetNotesResult:
type: object
@ -1243,6 +1212,12 @@ components:
version:
nullable: true
type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Note:
allOf:
- $ref: '#/components/schemas/BareNote'
@ -1262,6 +1237,23 @@ components:
- $ref: '#/components/schemas/PinnedEventBaseResponseBody'
- nullable: true
type: object
PersistTimelineResponse:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
PinnedEvent:
allOf:
- $ref: '#/components/schemas/BarePinnedEvent'
@ -1299,34 +1291,27 @@ components:
nullable: true
type: string
value:
nullable: true
type: string
Readable:
oneOf:
- nullable: true
type: string
- items:
type: string
nullable: true
type: array
ResolvedTimeline:
type: object
properties:
_data:
additionalProperties: true
type: object
_encoding:
alias_purpose:
$ref: '#/components/schemas/SavedObjectResolveAliasPurpose'
alias_target_id:
type: string
_events:
additionalProperties: true
type: object
_eventsCount:
type: number
_maxListeners:
additionalProperties: true
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
outcome:
$ref: '#/components/schemas/SavedObjectResolveOutcome'
timeline:
$ref: '#/components/schemas/TimelineSavedToReturnObject'
required:
- timeline
- outcome
ResponseNote:
type: object
properties:
@ -1361,6 +1346,17 @@ components:
- threat_match
- zeek
type: string
SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
SavedTimeline:
type: object
properties:
@ -1389,12 +1385,16 @@ components:
properties:
end:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
start:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
description:
nullable: true
type: string
@ -1483,6 +1483,18 @@ components:
updatedBy:
nullable: true
type: string
SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
SerializedFilterQueryResult:
type: object
properties:
@ -1532,27 +1544,63 @@ components:
TimelineResponse:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- $ref: '#/components/schemas/SavedTimelineWithSavedObjectId'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/PinnedEvent'
nullable: true
type: array
TimelineSavedToReturnObject:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/PinnedEvent'
nullable: true
type: array
savedObjectId:
type: string

View file

@ -263,18 +263,20 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
nullable: true
data:
type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- getOneTimeline
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: Indicates that the (template) timeline was found and returned.
summary: >-
Get an existing saved timeline or timeline template. This API is used to
@ -313,22 +315,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -389,20 +376,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates the timeline was successfully created.
'405':
content:
@ -419,6 +393,36 @@ paths:
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_copy:
get:
description: |
Copies and returns a timeline or timeline template.
operationId: CopyTimeline
requestBody:
content:
application/json:
schema:
type: object
properties:
timeline:
$ref: '#/components/schemas/SavedTimeline'
timelineIdToCopy:
type: string
required:
- timeline
- timelineIdToCopy
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates that the timeline has been successfully copied.
summary: Copies timeline or timeline template
tags:
- Security Timeline API
- 'access:securitySolution'
/api/timeline/_draft:
get:
operationId: GetDraftTimelines
@ -433,22 +437,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: Indicates that the draft timeline was successfully retrieved.
'403':
content:
@ -508,22 +497,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
$ref: '#/components/schemas/PersistTimelineResponse'
description: >-
Indicates that the draft timeline was successfully created. In the
event the user already has a draft timeline, the existing draft
@ -675,28 +649,14 @@ paths:
schema:
type: object
properties:
file:
allOf:
- $ref: '#/components/schemas/Readable'
- type: object
properties:
hapi:
type: object
properties:
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
file: {}
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- file
description: The timelines to import as a readable stream.
required: true
responses:
@ -704,12 +664,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
$ref: '#/components/schemas/ImportTimelineResult'
description: Indicates the import of timelines was successful.
'400':
content:
@ -767,7 +722,8 @@ paths:
properties:
prepackagedTimelines:
items:
$ref: '#/components/schemas/SavedTimeline'
$ref: '#/components/schemas/TimelineSavedToReturnObject'
nullable: true
type: array
timelinesToInstall:
items:
@ -790,12 +746,7 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
$ref: '#/components/schemas/ImportTimelineResult'
description: Indicates the installation of prepackaged timelines was successful.
'500':
content:
@ -833,18 +784,15 @@ paths:
content:
application/json:
schema:
type: object
properties:
data:
type: object
oneOf:
- type: object
properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
nullable: true
data:
$ref: '#/components/schemas/ResolvedTimeline'
required:
- getOneTimeline
required:
- data
- data
- additionalProperties: false
type: object
description: The (template) timeline has been found
'400':
description: The request is missing parameters
@ -912,35 +860,25 @@ paths:
schema:
type: object
properties:
data:
type: object
properties:
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timelines:
items:
$ref: '#/components/schemas/TimelineResponse'
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
customTemplateTimelineCount:
type: number
defaultTimelineCount:
type: number
elasticTemplateTimelineCount:
type: number
favoriteCount:
type: number
templateTimelineCount:
type: number
timeline:
items:
$ref: '#/components/schemas/TimelineResponse'
type: array
totalCount:
type: number
required:
- data
- timeline
- totalCount
description: Indicates that the (template) timelines were found and returned.
'400':
content:
@ -1012,30 +950,39 @@ components:
type: object
properties:
aggregatable:
nullable: true
type: boolean
category:
nullable: true
type: string
columnHeaderType:
nullable: true
type: string
description:
nullable: true
type: string
example:
oneOf:
- type: string
- type: number
nullable: true
type: string
id:
nullable: true
type: string
indexes:
items:
type: string
nullable: true
type: array
name:
nullable: true
type: string
placeholder:
nullable: true
type: string
searchable:
nullable: true
type: boolean
type:
nullable: true
type: string
DataProviderQueryMatch:
type: object
@ -1057,6 +1004,10 @@ components:
type: string
queryMatch:
$ref: '#/components/schemas/QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/DataProviderType'
nullable: true
DataProviderResult:
type: object
properties:
@ -1144,41 +1095,59 @@ components:
type: object
properties:
exists:
type: boolean
nullable: true
type: string
match_all:
nullable: true
type: string
meta:
nullable: true
type: object
properties:
alias:
nullable: true
type: string
controlledBy:
nullable: true
type: string
disabled:
nullable: true
type: boolean
field:
nullable: true
type: string
formattedValue:
nullable: true
type: string
index:
nullable: true
type: string
key:
nullable: true
type: string
negate:
nullable: true
type: boolean
params:
nullable: true
type: string
type:
nullable: true
type: string
value:
nullable: true
type: string
missing:
nullable: true
type: string
query:
nullable: true
type: string
range:
nullable: true
type: string
script:
nullable: true
type: string
GetNotesResult:
type: object
@ -1243,6 +1212,12 @@ components:
version:
nullable: true
type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Note:
allOf:
- $ref: '#/components/schemas/BareNote'
@ -1262,6 +1237,23 @@ components:
- $ref: '#/components/schemas/PinnedEventBaseResponseBody'
- nullable: true
type: object
PersistTimelineResponse:
type: object
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
PinnedEvent:
allOf:
- $ref: '#/components/schemas/BarePinnedEvent'
@ -1299,34 +1291,27 @@ components:
nullable: true
type: string
value:
nullable: true
type: string
Readable:
oneOf:
- nullable: true
type: string
- items:
type: string
nullable: true
type: array
ResolvedTimeline:
type: object
properties:
_data:
additionalProperties: true
type: object
_encoding:
alias_purpose:
$ref: '#/components/schemas/SavedObjectResolveAliasPurpose'
alias_target_id:
type: string
_events:
additionalProperties: true
type: object
_eventsCount:
type: number
_maxListeners:
additionalProperties: true
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
outcome:
$ref: '#/components/schemas/SavedObjectResolveOutcome'
timeline:
$ref: '#/components/schemas/TimelineSavedToReturnObject'
required:
- timeline
- outcome
ResponseNote:
type: object
properties:
@ -1361,6 +1346,17 @@ components:
- threat_match
- zeek
type: string
SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
SavedTimeline:
type: object
properties:
@ -1389,12 +1385,16 @@ components:
properties:
end:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
start:
oneOf:
- type: string
- type: number
- nullable: true
type: string
- nullable: true
type: number
description:
nullable: true
type: string
@ -1483,6 +1483,18 @@ components:
updatedBy:
nullable: true
type: string
SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
SerializedFilterQueryResult:
type: object
properties:
@ -1532,27 +1544,63 @@ components:
TimelineResponse:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- $ref: '#/components/schemas/SavedTimelineWithSavedObjectId'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/PinnedEvent'
nullable: true
type: array
TimelineSavedToReturnObject:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
eventIdToNoteIds:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
noteIds:
items:
type: string
nullable: true
type: array
notes:
items:
$ref: '#/components/schemas/Note'
nullable: true
type: array
pinnedEventIds:
items:
type: string
nullable: true
type: array
pinnedEventsSaveObject:
items:
$ref: '#/components/schemas/PinnedEvent'
nullable: true
type: array
savedObjectId:
type: string

View file

@ -9,9 +9,10 @@ import { FilterStateStore } from '@kbn/es-query';
import type { DataTableModel } from '@kbn/securitysolution-data-table';
import { VIEW_SELECTION } from '../../../common/constants';
import type { TimelineResult } from '../../../common/api/timeline';
import { TimelineId, TimelineTabs } from '../../../common/types/timeline';
import type { TimelineResponse } from '../../../common/api/timeline';
import {
type ColumnHeaderResult,
RowRendererIdEnum,
TimelineTypeEnum,
TimelineStatusEnum,
@ -1986,9 +1987,11 @@ export const mockDataTableModel: DataTableModel = {
},
};
export const mockGetOneTimelineResult: TimelineResult = {
export const mockGetOneTimelineResult: TimelineResponse = {
savedObjectId: 'ef579e40-jibber-jabber',
columns: timelineDefaults.columns.filter((column) => column.id !== 'event.action'),
columns: timelineDefaults.columns.filter(
(column) => column.id !== 'event.action'
) as ColumnHeaderResult[],
dateRange: { start: '2020-03-18T13:46:38.929Z', end: '2020-03-18T13:52:38.929Z' },
description: 'This is a sample rule description',
eventType: 'all',

View file

@ -50,8 +50,8 @@ import {
isNewTermsRule,
isThresholdRule,
} from '../../../../common/detection_engine/utils';
import type { TimelineResult } from '../../../../common/api/timeline';
import { TimelineId } from '../../../../common/types/timeline';
import type { TimelineResponse } from '../../../../common/api/timeline';
import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline';
import type {
SendAlertToTimelineActionProps,
@ -69,7 +69,7 @@ import { TimelineEventsQueries } from '../../../../common/search_strategy/timeli
import { timelineDefaults } from '../../../timelines/store/defaults';
import {
omitTypenameInTimeline,
formatTimelineResultToModel,
formatTimelineResponseToModel,
} from '../../../timelines/components/open_timeline/helpers';
import { convertKueryToElasticSearchQuery } from '../../../common/lib/kuery';
import { getField, getFieldKey } from '../../../helpers';
@ -983,11 +983,15 @@ export const sendAlertToTimelineAction = async ({
),
]);
const resultingTimeline: TimelineResult = getOr({}, 'data.getOneTimeline', responseTimeline);
const resultingTimeline: TimelineResponse = getOr(
{},
'data.getOneTimeline',
responseTimeline
);
const eventData: TimelineEventsDetailsItem[] = eventDataResp.data ?? [];
if (!isEmpty(resultingTimeline)) {
const timelineTemplate: TimelineResult = omitTypenameInTimeline(resultingTimeline);
const { timeline, notes } = formatTimelineResultToModel(
const timelineTemplate = omitTypenameInTimeline(resultingTimeline);
const { timeline, notes } = formatTimelineResponseToModel(
timelineTemplate,
true,
timelineTemplate.timelineType ?? TimelineTypeEnum.default

View file

@ -20,12 +20,16 @@ import {
isUntitled,
omitTypenameInTimeline,
useQueryTimelineById,
formatTimelineResultToModel,
formatTimelineResponseToModel,
} from './helpers';
import type { OpenTimelineResult } from './types';
import { TimelineId } from '../../../../common/types/timeline';
import type { RowRendererId } from '../../../../common/api/timeline';
import { TimelineTypeEnum, TimelineStatusEnum } from '../../../../common/api/timeline';
import {
TimelineTypeEnum,
TimelineStatusEnum,
type ColumnHeaderResult,
type RowRendererId,
} from '../../../../common/api/timeline';
import {
mockTimeline as mockSelectedTimeline,
mockTemplate as mockSelectedTemplate,
@ -379,7 +383,7 @@ describe('helpers', () => {
);
const timeline = {
savedObjectId: 'savedObject-1',
columns: columnsWithoutEventAction,
columns: columnsWithoutEventAction as ColumnHeaderResult[],
version: '1',
};
@ -396,7 +400,7 @@ describe('helpers', () => {
);
const timeline = {
savedObjectId: 'savedObject-1',
columns: columnsWithoutEventAction,
columns: columnsWithoutEventAction as ColumnHeaderResult[],
filters: [
{
meta: {
@ -568,7 +572,7 @@ describe('helpers', () => {
version: '1',
status: TimelineStatusEnum.active,
timelineType: TimelineTypeEnum.default,
columns: customColumns,
columns: customColumns as ColumnHeaderResult[],
};
const newTimeline = defaultTimelineToTimelineModel(
@ -691,7 +695,7 @@ describe('helpers', () => {
});
test('Do not override daterange if TimelineStatus is active', () => {
const { timeline } = formatTimelineResultToModel(
const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
args.duplicate,
args.timelineType
@ -744,7 +748,7 @@ describe('helpers', () => {
});
test('should not override daterange if TimelineStatus is active', () => {
const { timeline } = formatTimelineResultToModel(
const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
args.duplicate,
args.timelineType
@ -818,7 +822,7 @@ describe('helpers', () => {
});
test('override daterange if TimelineStatus is immutable', () => {
const { timeline } = formatTimelineResultToModel(
const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', template)),
args.duplicate,
args.timelineType

View file

@ -14,8 +14,8 @@ import { useCallback } from 'react';
import { useDiscoverInTimelineContext } from '../../../common/components/discover_in_timeline/use_discover_in_timeline_context';
import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
import type {
TimelineResult,
SingleTimelineResolveResponse,
TimelineResponse,
ResolvedTimeline,
ColumnHeaderResult,
FilterTimelineResult,
DataProviderResult,
@ -80,7 +80,7 @@ export const isUntitled = ({ title }: OpenTimelineResult): boolean =>
const omitTypename = (key: string, value: keyof TimelineModel) =>
key === '__typename' ? undefined : value;
export const omitTypenameInTimeline = (timeline: TimelineResult): TimelineResult =>
export const omitTypenameInTimeline = (timeline: TimelineResponse): TimelineResponse =>
JSON.parse(JSON.stringify(timeline), omitTypename);
const parseString = (params: string) => {
@ -164,7 +164,7 @@ const setPinnedEventIds = (duplicate: boolean, pinnedEventIds: string[] | null |
: {};
const getTemplateTimelineId = (
timeline: TimelineResult,
timeline: TimelineResponse,
duplicate: boolean,
targetTimelineType?: TimelineType
) => {
@ -200,7 +200,7 @@ const convertToDefaultField = ({ and, ...dataProvider }: DataProviderResult) =>
const getDataProviders = (
duplicate: boolean,
dataProviders: TimelineResult['dataProviders'],
dataProviders: TimelineResponse['dataProviders'],
timelineType?: TimelineType
) => {
if (duplicate && dataProviders && timelineType === TimelineTypeEnum.default) {
@ -214,7 +214,7 @@ const getDataProviders = (
};
export const getTimelineTitle = (
timeline: TimelineResult,
timeline: TimelineResponse,
duplicate: boolean,
timelineType?: TimelineType
) => {
@ -225,7 +225,7 @@ export const getTimelineTitle = (
};
export const getTimelineStatus = (
timeline: TimelineResult,
timeline: TimelineResponse,
duplicate: boolean,
timelineType?: TimelineType
) => {
@ -236,7 +236,7 @@ export const getTimelineStatus = (
};
export const defaultTimelineToTimelineModel = (
timeline: TimelineResult,
timeline: TimelineResponse,
duplicate: boolean,
timelineType?: TimelineType,
unifiedComponentsInTimelineDisabled?: boolean
@ -291,8 +291,8 @@ export const defaultTimelineToTimelineModel = (
);
};
export const formatTimelineResultToModel = (
timelineToOpen: TimelineResult,
export const formatTimelineResponseToModel = (
timelineToOpen: TimelineResponse,
duplicate: boolean = false,
timelineType?: TimelineType,
unifiedComponentsInTimelineDisabled?: boolean
@ -376,12 +376,12 @@ export const useQueryTimelineById = () => {
} else {
return Promise.resolve(resolveTimeline(timelineId))
.then((result) => {
const data: SingleTimelineResolveResponse['data'] | null = getOr(null, 'data', result);
const data: ResolvedTimeline | null = getOr(null, 'data', result);
if (!data) return;
const timelineToOpen = omitTypenameInTimeline(data.timeline);
const { timeline, notes } = formatTimelineResultToModel(
const { timeline, notes } = formatTimelineResponseToModel(
timelineToOpen,
duplicate,
timelineType,

View file

@ -10,7 +10,7 @@ import type { IconType } from '@elastic/eui';
import type { TimelineModel } from '../../store/model';
import type {
RowRendererId,
SingleTimelineResolveResponse,
ResolvedTimeline,
TimelineType,
TimelineStatus,
TemplateTimelineType,
@ -210,9 +210,9 @@ export interface OpenTimelineProps {
}
export interface ResolveTimelineConfig {
alias_target_id: SingleTimelineResolveResponse['data']['alias_target_id'];
outcome: SingleTimelineResolveResponse['data']['outcome'];
alias_purpose: SingleTimelineResolveResponse['data']['alias_purpose'];
alias_target_id: ResolvedTimeline['alias_target_id'];
outcome: ResolvedTimeline['outcome'];
alias_purpose: ResolvedTimeline['alias_purpose'];
}
export interface UpdateTimeline {
duplicate: boolean;

View file

@ -21,9 +21,9 @@ import type {
TimelineType,
TimelineStatus,
PageInfoTimeline,
TimelineResult,
TimelineResponse,
SortTimeline,
GetAllTimelineVariables,
GetTimelinesRequestQuery,
} from '../../../../common/api/timeline';
import { TimelineTypeEnum } from '../../../../common/api/timeline';
import { getAllTimelines } from '../api';
@ -59,7 +59,7 @@ export interface AllTimelinesVariables {
export const ALL_TIMELINE_QUERY_ID = 'FETCH_ALL_TIMELINES';
export const getAllTimeline = memoizeOne(
(_variables: string, timelines: TimelineResult[]): OpenTimelineResult[] =>
(_variables: string, timelines: TimelineResponse[]): OpenTimelineResult[] =>
timelines.map((timeline) => ({
created: timeline.created,
description: timeline.description,
@ -132,13 +132,15 @@ export const useGetAllTimeline = (): AllTimelinesArgs => {
loading: true,
}));
const variables: GetAllTimelineVariables = {
onlyUserFavorite,
pageInfo,
const variables: GetTimelinesRequestQuery = {
only_user_favorite: onlyUserFavorite ? 'true' : 'false',
page_size: pageInfo.pageSize.toString(),
page_index: pageInfo.pageIndex.toString(),
search,
sort,
status,
timelineType,
sort_field: sort.sortField,
sort_order: sort.sortOrder,
status: status || undefined,
timeline_type: timelineType,
};
const getAllTimelineResponse = await getAllTimelines(variables, abortCtrl.signal);
const totalCount = getAllTimelineResponse?.totalCount ?? 0;
@ -163,7 +165,7 @@ export const useGetAllTimeline = (): AllTimelinesArgs => {
setAllTimelines({
loading: false,
totalCount,
timelines: getAllTimeline(JSON.stringify(variables), timelines as TimelineResult[]),
timelines: getAllTimeline(JSON.stringify(variables), timelines as TimelineResponse[]),
customTemplateTimelineCount,
defaultTimelineCount,
elasticTemplateTimelineCount,

View file

@ -5,34 +5,30 @@
* 2.0.
*/
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
import { isEmpty } from 'lodash';
import { throwErrors } from '@kbn/cases-plugin/common';
import type { SavedSearch } from '@kbn/saved-search-plugin/common';
import type {
TimelineResponse,
TimelineErrorResponse,
ImportTimelineResultSchema,
AllTimelinesResponse,
SingleTimelineResponse,
SingleTimelineResolveResponse,
GetTimelinesArgs,
CleanDraftTimelinesResponse,
TimelineType,
PatchTimelineResponse,
CreateTimelinesResponse,
CopyTimelineResponse,
GetDraftTimelinesResponse,
GetTimelinesRequestQuery,
SavedTimeline,
} from '../../../common/api/timeline';
import {
TimelineResponseType,
ImportTimelineResult,
TimelineErrorResponse,
TimelineStatusEnum,
TimelineErrorResponseType,
importTimelineResultSchema,
allTimelinesResponse,
PersistFavoriteRouteResponse,
SingleTimelineResponseType,
type TimelineType,
TimelineTypeEnum,
ResolvedSingleTimelineResponseType,
GetTimelineResponse,
ResolveTimelineResponse,
GetTimelinesResponse,
PersistTimelineResponse,
} from '../../../common/api/timeline';
import {
TIMELINE_URL,
@ -48,15 +44,15 @@ import {
import { KibanaServices } from '../../common/lib/kibana';
import { ToasterError } from '../../common/components/toasters';
import { parseOrThrowErrorFactory } from '../../../common/timelines/zod_errors';
import type {
ExportDocumentsProps,
ImportDataProps,
ImportDataResponse,
} from '../../detection_engine/rule_management/logic';
import type { TimelineInput } from '../../../common/search_strategy';
interface RequestPostTimeline {
timeline: TimelineInput;
timeline: SavedTimeline;
signal?: AbortSignal;
}
@ -68,48 +64,33 @@ interface RequestPatchTimeline<T = string> extends RequestPostTimeline {
type RequestPersistTimeline = RequestPostTimeline & Partial<RequestPatchTimeline<null | string>>;
const createToasterPlainError = (message: string) => new ToasterError([message]);
const decodeTimelineResponse = (respTimeline?: TimelineResponse | TimelineErrorResponse) =>
pipe(
TimelineResponseType.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
const decodeSingleTimelineResponse = (respTimeline?: SingleTimelineResponse) =>
pipe(
SingleTimelineResponseType.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
const parseOrThrow = parseOrThrowErrorFactory(createToasterPlainError);
const decodeResolvedSingleTimelineResponse = (respTimeline?: SingleTimelineResolveResponse) =>
pipe(
ResolvedSingleTimelineResponseType.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
const decodeTimelineResponse = (respTimeline?: PersistTimelineResponse | TimelineErrorResponse) =>
parseOrThrow(PersistTimelineResponse)(respTimeline);
const decodeAllTimelinesResponse = (respTimeline: AllTimelinesResponse) =>
pipe(
allTimelinesResponse.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
const decodeSingleTimelineResponse = (respTimeline?: GetTimelineResponse) =>
parseOrThrow(GetTimelineResponse)(respTimeline);
const decodeResolvedSingleTimelineResponse = (respTimeline?: ResolveTimelineResponse) =>
parseOrThrow(ResolveTimelineResponse)(respTimeline);
const decodeGetTimelinesResponse = (respTimeline: GetTimelinesResponse) =>
parseOrThrow(GetTimelinesResponse)(respTimeline);
const decodeTimelineErrorResponse = (respTimeline?: TimelineErrorResponse) =>
pipe(
TimelineErrorResponseType.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
parseOrThrow(TimelineErrorResponse)(respTimeline);
const decodePrepackedTimelineResponse = (respTimeline?: ImportTimelineResultSchema) =>
pipe(
importTimelineResultSchema.decode(respTimeline),
fold(throwErrors(createToasterPlainError), identity)
);
const decodePrepackedTimelineResponse = (respTimeline?: ImportTimelineResult) =>
parseOrThrow(ImportTimelineResult)(respTimeline);
const decodeResponseFavoriteTimeline = (respTimeline?: PersistFavoriteRouteResponse) =>
PersistFavoriteRouteResponse.parse(respTimeline);
parseOrThrow(PersistFavoriteRouteResponse)(respTimeline);
const postTimeline = async ({
timeline,
}: RequestPostTimeline): Promise<TimelineResponse | TimelineErrorResponse> => {
}: RequestPostTimeline): Promise<CreateTimelinesResponse | TimelineErrorResponse> => {
let requestBody;
try {
requestBody = JSON.stringify({ timeline });
@ -117,7 +98,7 @@ const postTimeline = async ({
return Promise.reject(new Error(`Failed to stringify query: ${JSON.stringify(err)}`));
}
const response = await KibanaServices.get().http.post<TimelineResponse>(TIMELINE_URL, {
const response = await KibanaServices.get().http.post<CreateTimelinesResponse>(TIMELINE_URL, {
method: 'POST',
body: requestBody,
version: '2023-10-31',
@ -131,7 +112,7 @@ const patchTimeline = async ({
timeline,
version,
savedSearch,
}: RequestPatchTimeline): Promise<TimelineResponse | TimelineErrorResponse> => {
}: RequestPatchTimeline): Promise<PatchTimelineResponse | TimelineErrorResponse> => {
let response = null;
let requestBody = null;
try {
@ -153,7 +134,7 @@ const patchTimeline = async ({
}
try {
response = await KibanaServices.get().http.patch<TimelineResponse>(TIMELINE_URL, {
response = await KibanaServices.get().http.patch<PatchTimelineResponse>(TIMELINE_URL, {
method: 'PATCH',
body: requestBody,
version: '2023-10-31',
@ -175,7 +156,7 @@ export const copyTimeline = async ({
timelineId,
timeline,
savedSearch,
}: RequestPersistTimeline): Promise<TimelineResponse | TimelineErrorResponse> => {
}: RequestPersistTimeline): Promise<CopyTimelineResponse | TimelineErrorResponse> => {
let response = null;
let requestBody = null;
let newSavedSearchId = null;
@ -205,7 +186,7 @@ export const copyTimeline = async ({
}
try {
response = await KibanaServices.get().http.post<TimelineResponse>(TIMELINE_COPY_URL, {
response = await KibanaServices.get().http.post<CopyTimelineResponse>(TIMELINE_COPY_URL, {
method: 'POST',
body: requestBody,
version: '1',
@ -224,10 +205,10 @@ export const persistTimeline = async ({
timeline,
version,
savedSearch,
}: RequestPersistTimeline): Promise<TimelineResponse | TimelineErrorResponse> => {
}: RequestPersistTimeline): Promise<PersistTimelineResponse | TimelineErrorResponse> => {
try {
if (isEmpty(timelineId) && timeline.status === TimelineStatusEnum.draft && timeline) {
const temp: TimelineResponse | TimelineErrorResponse = await cleanDraftTimeline({
const temp: CleanDraftTimelinesResponse | TimelineErrorResponse = await cleanDraftTimeline({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
timelineType: timeline.timelineType!,
templateTimelineId: timeline.templateTimelineId ?? undefined,
@ -330,13 +311,16 @@ export const getDraftTimeline = async ({
timelineType,
}: {
timelineType: TimelineType;
}): Promise<TimelineResponse> => {
const response = await KibanaServices.get().http.get<TimelineResponse>(TIMELINE_DRAFT_URL, {
query: {
timelineType,
},
version: '2023-10-31',
});
}): Promise<GetDraftTimelinesResponse> => {
const response = await KibanaServices.get().http.get<GetDraftTimelinesResponse>(
TIMELINE_DRAFT_URL,
{
query: {
timelineType,
},
version: '2023-10-31',
}
);
return decodeTimelineResponse(response);
};
@ -349,7 +333,7 @@ export const cleanDraftTimeline = async ({
timelineType: TimelineType;
templateTimelineId?: string;
templateTimelineVersion?: number;
}): Promise<TimelineResponse | TimelineErrorResponse> => {
}): Promise<CleanDraftTimelinesResponse | TimelineErrorResponse> => {
let requestBody;
const templateTimelineInfo =
timelineType === TimelineTypeEnum.template
@ -366,16 +350,19 @@ export const cleanDraftTimeline = async ({
} catch (err) {
return Promise.reject(new Error(`Failed to stringify query: ${JSON.stringify(err)}`));
}
const response = await KibanaServices.get().http.post<TimelineResponse>(TIMELINE_DRAFT_URL, {
body: requestBody,
version: '2023-10-31',
});
const response = await KibanaServices.get().http.post<CleanDraftTimelinesResponse>(
TIMELINE_DRAFT_URL,
{
body: requestBody,
version: '2023-10-31',
}
);
return decodeTimelineResponse(response);
};
export const installPrepackedTimelines = async (): Promise<ImportTimelineResultSchema> => {
const response = await KibanaServices.get().http.post<ImportTimelineResultSchema>(
export const installPrepackedTimelines = async (): Promise<ImportTimelineResult> => {
const response = await KibanaServices.get().http.post<ImportTimelineResult>(
TIMELINE_PREPACKAGED_URL,
{
version: '2023-10-31',
@ -386,7 +373,7 @@ export const installPrepackedTimelines = async (): Promise<ImportTimelineResultS
};
export const getTimeline = async (id: string) => {
const response = await KibanaServices.get().http.get<SingleTimelineResponse>(TIMELINE_URL, {
const response = await KibanaServices.get().http.get<GetTimelineResponse>(TIMELINE_URL, {
query: {
id,
},
@ -397,7 +384,7 @@ export const getTimeline = async (id: string) => {
};
export const resolveTimeline = async (id: string) => {
const response = await KibanaServices.get().http.get<SingleTimelineResolveResponse>(
const response = await KibanaServices.get().http.get<ResolveTimelineResponse>(
TIMELINE_RESOLVE_URL,
{
query: {
@ -411,7 +398,7 @@ export const resolveTimeline = async (id: string) => {
};
export const getTimelineTemplate = async (templateTimelineId: string) => {
const response = await KibanaServices.get().http.get<SingleTimelineResponse>(TIMELINE_URL, {
const response = await KibanaServices.get().http.get<GetTimelineResponse>(TIMELINE_URL, {
query: {
template_timeline_id: templateTimelineId,
},
@ -421,24 +408,18 @@ export const getTimelineTemplate = async (templateTimelineId: string) => {
return decodeSingleTimelineResponse(response);
};
export const getAllTimelines = async (args: GetTimelinesArgs, abortSignal: AbortSignal) => {
const response = await KibanaServices.get().http.fetch<AllTimelinesResponse>(TIMELINES_URL, {
export const getAllTimelines = async (
query: GetTimelinesRequestQuery,
abortSignal: AbortSignal
) => {
const response = await KibanaServices.get().http.fetch<GetTimelinesResponse>(TIMELINES_URL, {
method: 'GET',
query: {
...(args.onlyUserFavorite ? { only_user_favorite: args.onlyUserFavorite } : {}),
...(args?.pageInfo?.pageSize ? { page_size: args.pageInfo.pageSize } : {}),
...(args?.pageInfo?.pageIndex ? { page_index: args.pageInfo.pageIndex } : {}),
...(args.search ? { search: args.search } : {}),
...(args?.sort?.sortField ? { sort_field: args?.sort?.sortField } : {}),
...(args?.sort?.sortOrder ? { sort_order: args?.sort?.sortOrder } : {}),
...(args.status ? { status: args.status } : {}),
...(args.timelineType ? { timeline_type: args.timelineType } : {}),
},
query,
signal: abortSignal,
version: '2023-10-31',
});
return decodeAllTimelinesResponse(response);
return decodeGetTimelinesResponse(response);
};
export const persistFavorite = async ({

View file

@ -6,10 +6,10 @@
*/
import { TableId } from '@kbn/securitysolution-data-table';
import type { TimelineResult } from '../../../common/api/timeline';
import type { TimelineResponse } from '../../../common/api/timeline';
import { DEFAULT_ALERTS_INDEX } from '../../../common/constants';
export const getTimelineQueryTypes = (timeline: TimelineResult) => ({
export const getTimelineQueryTypes = (timeline: TimelineResponse) => ({
hasQuery:
(timeline.kqlQuery != null &&
timeline.kqlQuery.filterQuery != null &&

View file

@ -66,6 +66,7 @@ describe('Timeline pinned event middleware', () => {
data: {
persistPinnedEventOnTimeline: {
code: 200,
eventId: testEventId,
},
},
});

View file

@ -74,7 +74,7 @@ export const addPinnedEventToTimelineMiddleware: (kibana: CoreStart) => Middlewa
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 || !('code' in response)) {
if (!response || !('eventId' in response)) {
return store.dispatch(
updateTimeline({
id: action.payload.id,

View file

@ -167,7 +167,7 @@ describe('Timeline save middleware', () => {
});
it('should show an error message when the call is unauthorized', async () => {
(persistTimeline as jest.Mock).mockResolvedValue({ data: { persistTimeline: { code: 403 } } });
(persistTimeline as jest.Mock).mockResolvedValue({ status_code: 403 });
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
@ -175,7 +175,7 @@ describe('Timeline save middleware', () => {
});
describe('#convertTimelineAsInput ', () => {
test('should return a TimelineInput instead of TimelineModel ', () => {
test('should return a SavedTimeline instead of TimelineModel ', () => {
const columns: TimelineModel['columns'] = [
{
columnHeaderType: 'not-filtered',

View file

@ -34,8 +34,11 @@ import { selectTimelineById } from '../selectors';
import * as i18n from '../../pages/translations';
import type { inputsModel } from '../../../common/store/inputs';
import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline';
import type { TimelineErrorResponse, TimelineResponse } from '../../../../common/api/timeline';
import type { TimelineInput } from '../../../../common/search_strategy';
import type {
TimelineErrorResponse,
PersistTimelineResponse,
SavedTimeline,
} from '../../../../common/api/timeline';
import type { TimelineModel } from '../model';
import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
import { refreshTimelines } from './helpers';
@ -83,6 +86,9 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
if (isTimelineErrorResponse(result)) {
const error = getErrorFromResponse(result);
switch (error?.errorCode) {
case 403:
store.dispatch(showCallOutUnauthorizedMsg());
break;
// conflict
case 409:
kibana.notifications.toasts.addDanger({
@ -108,11 +114,6 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
return;
}
if (response && response.code === 403) {
store.dispatch(showCallOutUnauthorizedMsg());
return;
}
refreshTimelines(store.getState());
store.dispatch(
@ -155,7 +156,7 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
return ret;
};
const timelineInput: TimelineInput = {
const timelineInput: SavedTimeline = {
columns: null,
dataProviders: null,
dataViewId: null,
@ -181,8 +182,8 @@ const timelineInput: TimelineInput = {
export const convertTimelineAsInput = (
timeline: TimelineModel,
timelineTimeRange: inputsModel.TimeRange
): TimelineInput =>
Object.keys(timelineInput).reduce<TimelineInput>((acc, key) => {
): SavedTimeline =>
Object.keys(timelineInput).reduce<SavedTimeline>((acc, key) => {
if (has(key, timeline)) {
if (key === 'kqlQuery') {
return set(`${key}.filterQuery`, get(`${key}.filterQuery`, timeline), acc);
@ -270,7 +271,7 @@ const convertToString = (obj: unknown) => {
}
};
type PossibleResponse = TimelineResponse | TimelineErrorResponse;
type PossibleResponse = PersistTimelineResponse | TimelineErrorResponse;
function isTimelineErrorResponse(response: PossibleResponse): response is TimelineErrorResponse {
return response && ('status_code' in response || 'statusCode' in response);

View file

@ -6,8 +6,7 @@
*/
import { transformError } from '@kbn/securitysolution-es-utils';
import { validate } from '@kbn/securitysolution-io-ts-utils';
import { checkTimelineStatusRt } from '../../../../../../common/api/timeline';
import { InstallPrepackedTimelinesRequestBody } from '../../../../../../common/api/timeline';
import { buildSiemResponse } from '../../../routes/utils';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
@ -69,10 +68,8 @@ export const getPrebuiltRulesAndTimelinesStatusRoute = (router: SecuritySolution
const frameworkRequest = await buildFrameworkRequest(context, request);
const prebuiltTimelineStatus = await checkTimelinesStatus(frameworkRequest);
const [validatedPrebuiltTimelineStatus] = validate(
prebuiltTimelineStatus,
checkTimelineStatusRt
);
const validatedPrebuiltTimelineStatus =
InstallPrepackedTimelinesRequestBody.parse(prebuiltTimelineStatus);
const responseBody: ReadPrebuiltRulesAndTimelinesStatusResponse = {
rules_custom_installed: customRules.total,

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import { validate } from '@kbn/securitysolution-io-ts-utils';
import { importTimelineResultSchema } from '../../../../../common/api/timeline';
import { stringifyZodError } from '@kbn/zod-helpers';
import { ImportTimelineResult } from '../../../../../common/api/timeline';
import type { SecuritySolutionApiRequestHandlerContext } from '../../../../types';
import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines';
@ -18,10 +18,10 @@ export const performTimelinesInstallation = async (
securitySolutionContext.getFrameworkRequest(),
true
);
const [result, error] = validate(timeline, importTimelineResultSchema);
const parsed = ImportTimelineResult.safeParse(timeline);
return {
result,
error,
result: parsed.data,
error: parsed.error && stringifyZodError(parsed.error),
};
};

View file

@ -6,7 +6,6 @@
*/
import path, { join, resolve } from 'path';
import type * as rt from 'io-ts';
import {
TIMELINE_DRAFT_URL,
@ -17,9 +16,9 @@ import {
} from '../../../../common/constants';
import type {
SavedTimeline,
patchTimelineSchema,
createTimelineSchema,
GetTimelineQuery,
PatchTimelineRequestBody,
CreateTimelinesRequestBody,
GetTimelineRequestQuery,
} from '../../../../common/api/timeline';
import {
type TimelineType,
@ -135,14 +134,14 @@ export const updateTemplateTimelineWithTimelineId = {
version: 'WzEyMjUsMV0=',
};
export const getCreateTimelinesRequest = (mockBody: rt.TypeOf<typeof createTimelineSchema>) =>
export const getCreateTimelinesRequest = (mockBody: CreateTimelinesRequestBody) =>
requestMock.create({
method: 'post',
path: TIMELINE_URL,
body: mockBody,
});
export const getUpdateTimelinesRequest = (mockBody: rt.TypeOf<typeof patchTimelineSchema>) =>
export const getUpdateTimelinesRequest = (mockBody: PatchTimelineRequestBody) =>
requestMock.create({
method: 'patch',
path: TIMELINE_URL,
@ -167,7 +166,7 @@ export const cleanDraftTimelinesRequest = (timelineType: TimelineType) =>
},
});
export const getTimelineRequest = (query?: GetTimelineQuery) =>
export const getTimelineRequest = (query?: GetTimelineRequestQuery) =>
requestMock.create({
method: 'get',
path: TIMELINE_URL,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { ResolvedTimelineWithOutcomeSavedObject } from '../../../../common/api/timeline';
import type { ResolvedTimeline } from '../../../../common/api/timeline';
import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline';
export const mockResolvedSavedObject = {
@ -117,7 +117,7 @@ export const mockPopulatedTimeline = {
pinnedEventsSaveObject: [],
};
export const mockResolveTimelineResponse: ResolvedTimelineWithOutcomeSavedObject = {
export const mockResolveTimelineResponse: ResolvedTimeline = {
timeline: mockPopulatedTimeline,
outcome: 'aliasMatch',
alias_target_id: 'new-saved-object-id',

View file

@ -6,10 +6,10 @@
*/
import { v4 as uuidv4 } from 'uuid';
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import type { ConfigType } from '../../../../..';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { TIMELINE_DRAFT_URL } from '../../../../../../common/constants';
@ -21,12 +21,13 @@ import {
persistTimeline,
} from '../../../saved_object/timelines';
import { draftTimelineDefaults } from '../../../utils/default_timeline';
import type { CleanDraftTimelinesResponse } from '../../../../../../common/api/timeline';
import {
CleanDraftTimelinesRequestBody,
TimelineTypeEnum,
} from '../../../../../../common/api/timeline';
export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.post({
path: TIMELINE_DRAFT_URL,
@ -42,7 +43,7 @@ export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<CleanDraftTimelinesResponse>> => {
const frameworkRequest = await buildFrameworkRequest(context, request);
const siemResponse = buildSiemResponse(response);

View file

@ -5,19 +5,22 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import type { ConfigType } from '../../../../..';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { TIMELINE_DRAFT_URL } from '../../../../../../common/constants';
import { buildFrameworkRequest } from '../../../utils/common';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { getDraftTimeline, persistTimeline } from '../../../saved_object/timelines';
import { draftTimelineDefaults } from '../../../utils/default_timeline';
import { getDraftTimelineSchema } from '../../../../../../common/api/timeline';
import {
GetDraftTimelinesRequestQuery,
type GetDraftTimelinesResponse,
} from '../../../../../../common/api/timeline';
export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.get({
path: TIMELINE_DRAFT_URL,
@ -29,11 +32,11 @@ export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _:
.addVersion(
{
validate: {
request: { query: buildRouteValidationWithExcess(getDraftTimelineSchema) },
request: { query: buildRouteValidationWithZod(GetDraftTimelinesRequestQuery) },
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<GetDraftTimelinesResponse>> => {
const frameworkRequest = await buildFrameworkRequest(context, request);
const siemResponse = buildSiemResponse(response);

View file

@ -28,25 +28,25 @@ import { persistNoteRoute, deleteNoteRoute, getNotesRoute } from './notes';
import { persistPinnedEventRoute } from './pinned_events';
export function registerTimelineRoutes(router: SecuritySolutionPluginRouter, config: ConfigType) {
createTimelinesRoute(router, config);
patchTimelinesRoute(router, config);
createTimelinesRoute(router);
patchTimelinesRoute(router);
importTimelinesRoute(router, config);
exportTimelinesRoute(router, config);
getDraftTimelinesRoute(router, config);
getTimelineRoute(router, config);
resolveTimelineRoute(router, config);
getTimelinesRoute(router, config);
cleanDraftTimelinesRoute(router, config);
deleteTimelinesRoute(router, config);
persistFavoriteRoute(router, config);
copyTimelineRoute(router, config);
getDraftTimelinesRoute(router);
getTimelineRoute(router);
resolveTimelineRoute(router);
getTimelinesRoute(router);
cleanDraftTimelinesRoute(router);
deleteTimelinesRoute(router);
persistFavoriteRoute(router);
copyTimelineRoute(router);
installPrepackedTimelinesRoute(router, config);
persistNoteRoute(router, config);
deleteNoteRoute(router, config);
getNotesRoute(router, config);
persistNoteRoute(router);
deleteNoteRoute(router);
getNotesRoute(router);
persistPinnedEventRoute(router, config);
persistPinnedEventRoute(router);
}

View file

@ -5,21 +5,20 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { NOTE_URL } from '../../../../../common/constants';
import type { ConfigType } from '../../../..';
import { buildSiemResponse } from '../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../utils/common';
import { DeleteNoteRequestBody, type DeleteNoteResponse } from '../../../../../common/api/timeline';
import { deleteNote } from '../../saved_object/notes';
export const deleteNoteRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => {
export const deleteNoteRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.delete({
path: NOTE_URL,
@ -35,7 +34,7 @@ export const deleteNoteRoute = (router: SecuritySolutionPluginRouter, config: Co
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<DeleteNoteResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
@ -55,9 +54,8 @@ export const deleteNoteRoute = (router: SecuritySolutionPluginRouter, config: Co
noteIds,
});
const body: DeleteNoteResponse = { data: {} };
return response.ok({
body,
body: { data: {} },
});
} catch (err) {
const error = transformError(err);

View file

@ -5,21 +5,20 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { NOTE_URL } from '../../../../../common/constants';
import type { ConfigType } from '../../../..';
import { buildSiemResponse } from '../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../utils/common';
import { getAllSavedNote, MAX_UNASSOCIATED_NOTES } from '../../saved_object/notes';
import { noteSavedObjectType } from '../../saved_object_mappings/notes';
import { GetNotesRequestQuery, type GetNotesResponse } from '../../../../../common/api/timeline';
export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const getNotesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.get({
path: NOTE_URL,
@ -35,7 +34,7 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigTyp
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<GetNotesResponse>> => {
try {
const queryParams = request.query;
const frameworkRequest = await buildFrameworkRequest(context, request);
@ -60,8 +59,7 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigTyp
perPage: MAX_UNASSOCIATED_NOTES,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
return response.ok({ body: res ?? {} });
}
} else {
const perPage = queryParams?.perPage ? parseInt(queryParams.perPage, 10) : 10;

View file

@ -5,14 +5,13 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { NOTE_URL } from '../../../../../common/constants';
import type { ConfigType } from '../../../..';
import { buildSiemResponse } from '../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../utils/common';
@ -22,7 +21,7 @@ import {
} from '../../../../../common/api/timeline';
import { persistNote } from '../../saved_object/notes';
export const persistNoteRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const persistNoteRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.patch({
path: NOTE_URL,
@ -38,7 +37,7 @@ export const persistNoteRoute = (router: SecuritySolutionPluginRouter, _: Config
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<PersistNoteRouteResponse>> => {
const siemResponse = buildSiemResponse(response);
try {

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
@ -12,8 +13,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../types';
import { PINNED_EVENT_URL } from '../../../../../common/constants';
import type { ConfigType } from '../../../..';
import { buildSiemResponse } from '../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../utils/common';
@ -23,10 +22,7 @@ import {
} from '../../../../../common/api/timeline';
import { persistPinnedEventOnTimeline } from '../../saved_object/pinned_events';
export const persistPinnedEventRoute = (
router: SecuritySolutionPluginRouter,
config: ConfigType
) => {
export const persistPinnedEventRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.patch({
path: PINNED_EVENT_URL,
@ -42,7 +38,11 @@ export const persistPinnedEventRoute = (
},
version: '2023-10-31',
},
async (context, request, response) => {
async (
context,
request,
response
): Promise<IKibanaResponse<PersistPinnedEventRouteResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
@ -58,12 +58,10 @@ export const persistPinnedEventRoute = (
timelineId
);
const body: PersistPinnedEventRouteResponse = {
data: { persistPinnedEventOnTimeline: res },
};
return response.ok({
body,
body: {
data: { persistPinnedEventOnTimeline: res },
},
});
} catch (err) {
const error = transformError(err);

View file

@ -20,7 +20,6 @@ import {
import * as helpers from './helpers';
import { importTimelines } from '../../timelines/import_timelines/helpers';
import { buildFrameworkRequest } from '../../../utils/common';
import type { ImportTimelineResultSchema } from '../../../../../../common/api/timeline';
jest.mock('../../timelines/import_timelines/helpers');
@ -231,9 +230,8 @@ describe('installPrepackagedTimelines', () => {
);
expect(
(result as ImportTimelineResultSchema).errors[0].error.message.includes(
'read prepackaged timelines error:'
)
'errors' in result &&
result.errors?.[0].error?.message?.includes('read prepackaged timelines error:')
).toBeTruthy();
});
});

View file

@ -8,7 +8,7 @@
import path, { join, resolve } from 'path';
import { Readable } from 'stream';
import type { ImportTimelineResultSchema } from '../../../../../../common/api/timeline';
import type { ImportTimelineResult } from '../../../../../../common/api/timeline';
import type { FrameworkRequest } from '../../../../framework';
@ -22,7 +22,7 @@ export const installPrepackagedTimelines = async (
isImmutable: boolean,
filePath?: string,
fileName?: string
): Promise<ImportTimelineResultSchema | Error> => {
): Promise<ImportTimelineResult | Error> => {
let readStream;
const dir = resolve(
join(
@ -47,7 +47,7 @@ export const installPrepackagedTimelines = async (
],
};
}
return loadData<null, ImportTimelineResultSchema>(readStream, <T>(docs: T) =>
return loadData<null, ImportTimelineResult>(readStream, <T>(docs: T) =>
docs instanceof Readable
? importTimelines(docs, maxTimelineImportExportSize, frameworkRequest, isImmutable)
: Promise.reject(new Error(`read prepackaged timelines error`))

View file

@ -6,14 +6,17 @@
*/
import { transformError } from '@kbn/securitysolution-es-utils';
import { validate } from '@kbn/securitysolution-io-ts-utils';
import { checkTimelineStatusRt } from '../../../../../../common/api/timeline';
import type { IKibanaResponse } from '@kbn/core-http-server';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_PREPACKAGED_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../../config';
import {
InstallPrepackedTimelinesRequestBody,
type InstallPrepackedTimelinesResponse,
} from '../../../../../../common/api/timeline';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { installPrepackagedTimelines } from './helpers';
@ -45,23 +48,24 @@ export const installPrepackedTimelinesRoute = (
validate: {},
version: '2023-10-31',
},
async (context, request, response) => {
async (
context,
request,
response
): Promise<IKibanaResponse<InstallPrepackedTimelinesResponse>> => {
try {
const frameworkRequest = await buildFrameworkRequest(context, request);
const prepackagedTimelineStatus = await checkTimelinesStatus(frameworkRequest);
const [validatedprepackagedTimelineStatus, prepackagedTimelineStatusError] = validate(
prepackagedTimelineStatus,
checkTimelineStatusRt
);
if (prepackagedTimelineStatusError != null) {
throw prepackagedTimelineStatusError;
const installResult =
InstallPrepackedTimelinesRequestBody.safeParse(prepackagedTimelineStatus);
if (installResult.error) {
throw installResult.error;
}
const timelinesToInstalled =
validatedprepackagedTimelineStatus?.timelinesToInstall.length ?? 0;
const timelinesNotUpdated =
validatedprepackagedTimelineStatus?.timelinesToUpdate.length ?? 0;
const timelinesToInstalled = installResult.data.timelinesToInstall.length ?? 0;
const timelinesNotUpdated = installResult.data.timelinesToUpdate.length ?? 0;
let res = null;
if (timelinesToInstalled > 0 || timelinesNotUpdated > 0) {

View file

@ -5,10 +5,13 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import type { ConfigType } from '../../../../..';
import { copyTimelineSchema } from '../../../../../../common/api/timeline';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import {
CopyTimelineRequestBody,
type CopyTimelineResponse,
} from '../../../../../../common/api/timeline';
import { copyTimeline } from '../../../saved_object/timelines';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_COPY_URL } from '../../../../../../common/constants';
@ -16,7 +19,7 @@ import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common';
export const copyTimelineRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const copyTimelineRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.post({
path: TIMELINE_COPY_URL,
@ -29,17 +32,16 @@ export const copyTimelineRoute = (router: SecuritySolutionPluginRouter, _: Confi
{
version: '1',
validate: {
request: { body: buildRouteValidationWithExcess(copyTimelineSchema) },
request: { body: buildRouteValidationWithZod(CopyTimelineRequestBody) },
},
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<CopyTimelineResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
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 } },
});

View file

@ -10,8 +10,9 @@ import { isEmpty } from 'lodash/fp';
import moment from 'moment';
import { timeline as timelineLib, pinnedEvent as pinnedEventLib } from '../../../saved_object';
import type { FrameworkRequest } from '../../../../framework';
import type { ResponseTimeline, SavedTimeline, Note } from '../../../../../../common/api/timeline';
import type { SavedTimeline, Note } from '../../../../../../common/api/timeline';
import { persistNotes } from '../../../saved_object/notes/persist_notes';
import type { InternalTimelineResponse } from '../../../saved_object/timelines';
interface CreateTimelineProps {
frameworkRequest: FrameworkRequest;
@ -21,7 +22,7 @@ interface CreateTimelineProps {
overrideNotesOwner?: boolean;
pinnedEventIds?: string[] | null;
notes?: Note[];
existingNoteIds?: string[];
existingNoteIds?: string[] | null;
isImmutable?: boolean;
}
@ -40,7 +41,7 @@ export const createTimelines = async ({
existingNoteIds = [],
isImmutable,
overrideNotesOwner = true,
}: CreateTimelineProps): Promise<ResponseTimeline> => {
}: CreateTimelineProps): Promise<InternalTimelineResponse> => {
const timerangeStart = isImmutable
? moment().subtract(24, 'hours').toISOString()
: timeline.dateRange?.start;

View file

@ -7,16 +7,13 @@
import { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core/server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../..';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { createTimelineSchema } from '../../../../../../common/api/timeline';
import {
buildFrameworkRequest,
CompareTimelinesStatus,
@ -24,11 +21,14 @@ import {
} from '../../../utils/common';
import { DEFAULT_ERROR } from '../../../utils/failure_cases';
import { createTimelines } from './helpers';
import type { CreateTimelinesResponse } from '../../../../../../common/api/timeline';
import {
CreateTimelinesRequestBody,
type CreateTimelinesResponse,
} from '../../../../../../common/api/timeline';
export * from './helpers';
export const createTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const createTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.post({
path: TIMELINE_URL,
@ -42,7 +42,7 @@ export const createTimelinesRoute = (router: SecuritySolutionPluginRouter, _: Co
version: '2023-10-31',
validate: {
request: {
body: buildRouteValidationWithExcess(createTimelineSchema),
body: buildRouteValidationWithZod(CreateTimelinesRequestBody),
},
},
},

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { ConfigType } from '../../../../..';
import {
DeleteTimelinesRequestBody,
type DeleteTimelinesResponse,
@ -19,7 +19,7 @@ import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common';
import { deleteTimeline } from '../../../saved_object/timelines';
export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => {
export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.delete({
path: TIMELINE_URL,
@ -35,7 +35,7 @@ export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
request: { body: buildRouteValidationWithZod(DeleteTimelinesRequestBody) },
},
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<DeleteTimelinesResponse>> => {
const siemResponse = buildSiemResponse(response);
try {

View file

@ -9,11 +9,11 @@ import { omit } from 'lodash/fp';
import { transformDataToNdjson } from '@kbn/securitysolution-utils';
import type {
ExportedTimelines,
ExportedNotes,
ExportTimelineNotFoundError,
Note,
PinnedEvent,
TimelineResponse,
} from '../../../../../../common/api/timeline';
import type { FrameworkRequest } from '../../../../framework';
@ -45,7 +45,7 @@ const getPinnedEventsIdsByTimelineId = (currentPinnedEvents: PinnedEvent[]): str
const getTimelinesFromObjects = async (
request: FrameworkRequest,
ids?: string[] | null
): Promise<Array<ExportedTimelines | ExportTimelineNotFoundError>> => {
): Promise<Array<TimelineResponse | ExportTimelineNotFoundError>> => {
const { timelines, errors } = await getSelectedTimelines(request, ids);
const exportedIds = timelines.map((t) => t.savedObjectId);
@ -65,7 +65,7 @@ const getTimelinesFromObjects = async (
[]
);
const myResponse = exportedIds.reduce<ExportedTimelines[]>((acc, timelineId) => {
const myResponse = exportedIds.reduce<TimelineResponse[]>((acc, timelineId) => {
const myTimeline = timelines.find((t) => t.savedObjectId === timelineId);
if (myTimeline != null) {
const timelineNotes = myNotes.filter((n) => n.timelineId === timelineId);

View file

@ -5,11 +5,7 @@
* 2.0.
*/
import {
serverMock,
requestContextMock,
createMockConfig,
} from '../../../../detection_engine/routes/__mocks__';
import { serverMock, requestContextMock } from '../../../../detection_engine/routes/__mocks__';
import { getTimelineOrNull, getTimelineTemplateOrNull } from '../../../saved_object/timelines';
import { getTimelineRequest } from '../../../__mocks__/request_responses';
@ -33,7 +29,7 @@ describe('get timeline', () => {
server = serverMock.create();
context = requestContextMock.createTools().context;
getTimelineRoute(server.router, createMockConfig());
getTimelineRoute(server.router);
});
test('should call getTimelineTemplateOrNull if templateTimelineId is given', async () => {

View file

@ -5,25 +5,24 @@
* 2.0.
*/
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../..';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common';
import { getTimelineQuerySchema } from '../../../../../../common/api/timeline';
import { getTimelineTemplateOrNull, getTimelineOrNull } from '../../../saved_object/timelines';
import type {
TimelineSavedObject,
ResolvedTimelineWithOutcomeSavedObject,
import {
GetTimelineRequestQuery,
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, _: ConfigType) => {
export const getTimelineRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.get({
path: TIMELINE_URL,
@ -36,16 +35,16 @@ export const getTimelineRoute = (router: SecuritySolutionPluginRouter, _: Config
{
version: '2023-10-31',
validate: {
request: { query: buildRouteValidationWithExcess(getTimelineQuerySchema) },
request: { query: buildRouteValidationWithZod(GetTimelineRequestQuery) },
},
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<GetTimelineResponse>> => {
try {
const frameworkRequest = await buildFrameworkRequest(context, request);
const query = request.query ?? {};
const { template_timeline_id: templateTimelineId, id } = query;
let res: TimelineSavedObject | ResolvedTimelineWithOutcomeSavedObject | null = null;
let res: TimelineResponse | ResolvedTimeline | null = null;
if (templateTimelineId != null && id == null) {
res = await getTimelineTemplateOrNull(frameworkRequest, templateTimelineId);

View file

@ -5,11 +5,7 @@
* 2.0.
*/
import {
serverMock,
requestContextMock,
createMockConfig,
} from '../../../../detection_engine/routes/__mocks__';
import { serverMock, requestContextMock } from '../../../../detection_engine/routes/__mocks__';
import { getAllTimeline } from '../../../saved_object/timelines';
import { getTimelineRequest } from '../../../__mocks__/request_responses';
import { getTimelinesRoute } from '.';
@ -29,7 +25,7 @@ describe('get all timelines', () => {
server = serverMock.create();
context = requestContextMock.createTools().context;
getTimelinesRoute(server.router, createMockConfig());
getTimelinesRoute(server.router);
});
test('should get the total count', async () => {

View file

@ -5,24 +5,23 @@
* 2.0.
*/
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import type { IKibanaResponse } from '@kbn/core-http-server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINES_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../..';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { CustomHttpRequestError } from '../../../../../utils/custom_http_request_error';
import { buildFrameworkRequest, escapeHatch, throwErrors } from '../../../utils/common';
import { buildFrameworkRequest } from '../../../utils/common';
import { getAllTimeline } from '../../../saved_object/timelines';
import { getTimelinesQuerySchema } from '../../../../../../common/api/timeline';
import {
GetTimelinesRequestQuery,
type GetTimelinesResponse,
} from '../../../../../../common/api/timeline';
export const getTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const getTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.get({
path: TIMELINES_URL,
@ -34,27 +33,23 @@ export const getTimelinesRoute = (router: SecuritySolutionPluginRouter, _: Confi
.addVersion(
{
validate: {
request: { query: escapeHatch },
request: { query: buildRouteValidationWithZod(GetTimelinesRequestQuery) },
},
version: '2023-10-31',
},
async (context, request, response) => {
const customHttpRequestError = (message: string) =>
new CustomHttpRequestError(message, 400);
async (context, request, response): Promise<IKibanaResponse<GetTimelinesResponse>> => {
try {
const frameworkRequest = await buildFrameworkRequest(context, request);
const queryParams = pipe(
getTimelinesQuerySchema.decode(request.query),
fold(throwErrors(customHttpRequestError), identity)
);
const onlyUserFavorite = queryParams?.only_user_favorite === 'true' ? true : false;
const pageSize = queryParams?.page_size ? parseInt(queryParams.page_size, 10) : null;
const pageIndex = queryParams?.page_index ? parseInt(queryParams.page_index, 10) : null;
const search = queryParams?.search ?? null;
const sortField = queryParams?.sort_field ?? null;
const sortOrder = queryParams?.sort_order ?? null;
const status = queryParams?.status ?? null;
const timelineType = queryParams?.timeline_type ?? null;
const onlyUserFavorite = request.query?.only_user_favorite === 'true';
const pageSize = request.query?.page_size ? parseInt(request.query.page_size, 10) : null;
const pageIndex = request.query?.page_index
? parseInt(request.query.page_index, 10)
: null;
const search = request.query?.search ?? null;
const sortField = request.query?.sort_field ?? null;
const sortOrder = request.query?.sort_order ?? null;
const status = request.query?.status ?? null;
const timelineType = request.query?.timeline_type ?? null;
const sort =
sortField && sortOrder
? {

View file

@ -5,11 +5,7 @@
* 2.0.
*/
import type * as rt from 'io-ts';
import type { Transform } from 'stream';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { createConcatStream, createSplitStream, createMapStream } from '@kbn/utils';
import { BadRequestError } from '@kbn/securitysolution-es-utils';
import {
@ -19,23 +15,15 @@ import {
} from '../../../../../utils/read_stream/create_stream_from_ndjson';
import type { ImportTimelineResponse } from './types';
import { ImportTimelinesSchemaRt } from '../../../../../../common/api/timeline';
import { throwErrors } from '../../../utils/common';
import { ImportTimelines } from '../../../../../../common/api/timeline';
import { parseOrThrowErrorFactory } from '../../../../../../common/timelines/zod_errors';
type ErrorFactory = (message: string) => Error;
const createPlainError = (message: string) => new Error(message);
const parseOrThrow = parseOrThrowErrorFactory(createPlainError);
export const createPlainError = (message: string) => new Error(message);
export const decodeOrThrow =
<A, O, I>(runtimeType: rt.Type<A, O, I>, createError: ErrorFactory = createPlainError) =>
(inputValue: I) =>
pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity));
export const validateTimelines = (): Transform =>
const validateTimelines = (): Transform =>
createMapStream((obj: ImportTimelineResponse) =>
obj instanceof Error
? new BadRequestError(obj.message)
: decodeOrThrow(ImportTimelinesSchemaRt)(obj)
obj instanceof Error ? new BadRequestError(obj.message) : parseOrThrow(ImportTimelines)(obj)
);
export const createTimelinesStreamFromNdJson = (ruleLimit: number) => {
return [

View file

@ -10,12 +10,8 @@ import type { Readable } from 'stream';
import { v4 as uuidv4 } from 'uuid';
import { createPromiseFromStreams } from '@kbn/utils';
import { validate } from '@kbn/securitysolution-io-ts-utils';
import type { ImportTimelineResultSchema } from '../../../../../../common/api/timeline';
import {
importTimelineResultSchema,
TimelineStatusEnum,
} from '../../../../../../common/api/timeline';
import { stringifyZodError } from '@kbn/zod-helpers';
import { ImportTimelineResult, TimelineStatusEnum } from '../../../../../../common/api/timeline';
import type { BulkError } from '../../../../detection_engine/routes/utils';
import { createBulkErrorObject } from '../../../../detection_engine/routes/utils';
@ -88,7 +84,7 @@ export const importTimelines = async (
maxTimelineImportExportSize: number,
frameworkRequest: FrameworkRequest,
isImmutable?: boolean
): Promise<ImportTimelineResultSchema | Error> => {
): Promise<ImportTimelineResult | Error> => {
const readStream = createTimelinesStreamFromNdJson(maxTimelineImportExportSize);
const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([file, ...readStream]);
@ -262,17 +258,17 @@ export const importTimelines = async (
const timelinesUpdated = importTimelineResponse.filter(
(resp) => isImportRegular(resp) && resp.action === 'updateViaImport'
);
const importTimelinesRes: ImportTimelineResultSchema = {
const importTimelinesRes: ImportTimelineResult = {
success: errorsResp.length === 0,
success_count: successes.length,
errors: errorsResp,
timelines_installed: timelinesInstalled.length ?? 0,
timelines_updated: timelinesUpdated.length ?? 0,
};
const [validated, errors] = validate(importTimelinesRes, importTimelineResultSchema);
if (errors != null || validated == null) {
return new Error(errors || 'Import timeline error');
const parseResult = ImportTimelineResult.safeParse(importTimelinesRes);
if (parseResult.success && parseResult.data) {
return parseResult.data;
} else {
return validated;
return new Error(stringifyZodError(parseResult.error) || 'Import timeline error');
}
};

View file

@ -39,7 +39,6 @@ import {
describe('import timelines', () => {
let server: ReturnType<typeof serverMock.create>;
let request: ReturnType<typeof requestMock.create>;
let securitySetup: SecurityPluginSetup;
let { context } = requestContextMock.createTools();
let mockGetTimeline: jest.Mock;
@ -452,48 +451,6 @@ describe('import timelines', () => {
});
});
});
describe('request validation', () => {
beforeEach(() => {
jest.doMock('../../../saved_object/timelines', () => {
return {
getTimelineOrNull: mockGetTimeline.mockReturnValue(null),
persistTimeline: mockPersistTimeline.mockReturnValue({
timeline: { savedObjectId: '79deb4c0-6bc1-11ea-9999-f5341fb7a189' },
}),
};
});
jest.doMock('../../../saved_object/pinned_events', () => {
return {
savePinnedEvents: mockPersistPinnedEventOnTimeline.mockReturnValue(
new Error('Test error')
),
};
});
jest.doMock('../../../saved_object/notes/saved_object', () => {
return {
persistNote: mockPersistNote,
};
});
});
test('disallows invalid query', async () => {
request = requestMock.create({
method: 'post',
path: TIMELINE_EXPORT_URL,
body: { id: 'someId' },
});
const importTimelinesRoute = jest.requireActual('.').importTimelinesRoute;
importTimelinesRoute(server.router, createMockConfig(), securitySetup);
const result = server.validate(request);
expect(result.badRequest).toHaveBeenCalledWith(
'Invalid value {"id":"someId"}, excess properties: ["id"]'
);
});
});
});
describe('import timeline templates', () => {
@ -904,7 +861,7 @@ describe('import timeline templates', () => {
request = requestMock.create({
method: 'post',
path: TIMELINE_EXPORT_URL,
body: { id: 'someId' },
body: { isImmutable: 1 },
});
const importTimelinesRoute = jest.requireActual('.').importTimelinesRoute;
@ -912,7 +869,7 @@ describe('import timeline templates', () => {
const result = server.validate(request);
expect(result.badRequest).toHaveBeenCalledWith(
'Invalid value {"id":"someId"}, excess properties: ["id"]'
"isImmutable: Expected 'true' | 'false', received number"
);
});
});

View file

@ -7,18 +7,23 @@
import { extname } from 'path';
import type { Readable } from 'stream';
import { get } from 'lodash/fp';
import type { IKibanaResponse } from '@kbn/core-http-server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_IMPORT_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../../config';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { importTimelines } from './helpers';
import { ImportTimelinesPayloadSchemaRt } from '../../../../../../common/api/timeline';
import {
ImportTimelinesRequestBody,
type ImportTimelinesResponse,
} from '../../../../../../common/api/timeline';
import { buildFrameworkRequest } from '../../../utils/common';
export { importTimelines } from './helpers';
@ -39,11 +44,13 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
.addVersion(
{
validate: {
request: { body: buildRouteValidationWithExcess(ImportTimelinesPayloadSchemaRt) },
request: {
body: buildRouteValidationWithZod(ImportTimelinesRequestBody),
},
},
version: '2023-10-31',
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<ImportTimelinesResponse>> => {
try {
const siemResponse = buildSiemResponse(response);
const savedObjectsClient = (await context.core).savedObjects.client;
@ -52,7 +59,7 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
}
const { file, isImmutable } = request.body;
const { filename } = file.hapi;
const filename = extractFilename(file);
const fileExtension = extname(filename).toLowerCase();
if (fileExtension !== '.ndjson') {
@ -69,8 +76,11 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
frameworkRequest,
isImmutable === 'true'
);
if (typeof res !== 'string') return response.ok({ body: res ?? {} });
else throw res;
if (res instanceof Error || typeof res === 'string') {
throw res;
} else {
return response.ok({ body: res ?? {} });
}
} catch (err) {
const error = transformError(err);
const siemResponse = buildSiemResponse(response);
@ -82,3 +92,11 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
}
);
};
function extractFilename(fileObj: unknown) {
const filename = get('hapi.filename', fileObj);
if (filename && typeof filename === 'string') {
return filename;
}
throw new Error('`filename` missing in file');
}

View file

@ -7,22 +7,22 @@
import { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core/server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_URL } from '../../../../../../common/constants';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import type { ConfigType } from '../../../../..';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { patchTimelineSchema } from '../../../../../../common/api/timeline';
import {
PatchTimelineRequestBody,
type PatchTimelineResponse,
} from '../../../../../../common/api/timeline';
import { buildFrameworkRequest, TimelineStatusActions } from '../../../utils/common';
import { createTimelines } from '../create_timelines';
import { CompareTimelinesStatus } from '../../../utils/compare_timelines_status';
import type { PatchTimelinesResponse } from '../../../../../../common/api/timeline';
export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.patch({
path: TIMELINE_URL,
@ -34,11 +34,11 @@ export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter, _: Con
.addVersion(
{
validate: {
request: { body: buildRouteValidationWithExcess(patchTimelineSchema) },
request: { body: buildRouteValidationWithZod(PatchTimelineRequestBody) },
},
version: '2023-10-31',
},
async (context, request, response): Promise<IKibanaResponse<PatchTimelinesResponse>> => {
async (context, request, response): Promise<IKibanaResponse<PatchTimelineResponse>> => {
const siemResponse = buildSiemResponse(response);
try {

View file

@ -5,14 +5,13 @@
* 2.0.
*/
import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_FAVORITE_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../..';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common';
@ -23,7 +22,7 @@ import {
TimelineTypeEnum,
} from '../../../../../../common/api/timeline';
export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => {
export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.patch({
path: TIMELINE_FAVORITE_URL,
@ -39,7 +38,11 @@ export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter, _: Co
request: { body: buildRouteValidationWithZod(PersistFavoriteRouteRequestBody) },
},
},
async (context, request, response) => {
async (
context,
request,
response
): Promise<IKibanaResponse<PersistFavoriteRouteResponse>> => {
const siemResponse = buildSiemResponse(response);
try {

View file

@ -6,24 +6,24 @@
*/
import { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core-http-server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_RESOLVE_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../..';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common';
import { getTimelineQuerySchema } from '../../../../../../common/api/timeline';
import { getTimelineTemplateOrNull, resolveTimelineOrNull } from '../../../saved_object/timelines';
import type {
SavedTimeline,
ResolvedTimelineWithOutcomeSavedObject,
import {
ResolveTimelineRequestQuery,
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, _: ConfigType) => {
export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned
.get({
path: TIMELINE_RESOLVE_URL,
@ -36,16 +36,16 @@ export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter, _: Co
{
version: '2023-10-31',
validate: {
request: { query: buildRouteValidationWithExcess(getTimelineQuerySchema) },
request: { query: buildRouteValidationWithZod(ResolveTimelineRequestQuery) },
},
},
async (context, request, response) => {
async (context, request, response): Promise<IKibanaResponse<ResolveTimelineResponse>> => {
try {
const frameworkRequest = await buildFrameworkRequest(context, request);
const query = request.query ?? {};
const { template_timeline_id: templateTimelineId, id } = query;
let res: SavedTimeline | ResolvedTimelineWithOutcomeSavedObject | null = null;
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

View file

@ -13,7 +13,7 @@ import type { Note } from '../../../../../common/api/timeline';
export const persistNotes = async (
frameworkRequest: FrameworkRequest,
timelineSavedObjectId: string,
existingNoteIds?: string[],
existingNoteIds?: string[] | null,
newNotes?: Note[],
overrideOwner: boolean = true
) => {

View file

@ -16,7 +16,7 @@ import {
SavedObjectTimelineType,
SavedObjectTimelineStatus,
} from '../../../../../common/types/timeline/saved_object';
import type { TimelineSavedObject } from '../../../../../common/api/timeline';
import type { TimelineResponse } from '../../../../../common/api/timeline';
import {
type TimelineType,
TimelineTypeEnum,
@ -49,7 +49,7 @@ const getTimelineTypeAndStatus = (
};
};
export const convertSavedObjectToSavedTimeline = (savedObject: unknown): TimelineSavedObject =>
export const convertSavedObjectToSavedTimeline = (savedObject: unknown): TimelineResponse =>
pipe(
TimelineSavedObjectWithDraftRuntime.decode(savedObject),
map((savedTimeline) => {

View file

@ -23,8 +23,8 @@ import { getNotesByTimelineId, persistNote } from '../notes/saved_object';
import { getAllPinnedEventsByTimelineId, persistPinnedEventOnTimeline } from '../pinned_events';
import { TimelineTypeEnum } from '../../../../../common/api/timeline';
import type {
AllTimelinesResponse,
ResolvedTimelineWithOutcomeSavedObject,
GetTimelinesResponse,
ResolvedTimeline,
SavedTimeline,
} from '../../../../../common/api/timeline';
import {
@ -141,7 +141,7 @@ describe('saved_object', () => {
pageSize: 10,
pageIndex: 1,
};
let result = null as unknown as AllTimelinesResponse;
let result = null as unknown as GetTimelinesResponse;
beforeEach(async () => {
(convertSavedObjectToSavedTimeline as jest.Mock).mockReturnValue(mockGetTimelineValue);
mockFindSavedObject = jest
@ -275,7 +275,7 @@ describe('saved_object', () => {
describe('resolveTimelineOrNull', () => {
let mockResolveSavedObject: jest.Mock;
let mockRequest: FrameworkRequest;
let result: ResolvedTimelineWithOutcomeSavedObject | null = null;
let result: ResolvedTimeline | null = null;
beforeEach(async () => {
(convertSavedObjectToSavedTimeline as jest.Mock).mockReturnValue(mockResolvedTimeline);
mockResolveSavedObject = jest.fn().mockReturnValue(mockResolvedSavedObject);

View file

@ -19,20 +19,17 @@ import type {
Note,
BareNote,
PinnedEvent,
AllTimelinesResponse,
GetTimelinesResponse,
ExportTimelineNotFoundError,
PageInfoTimeline,
ResponseTimelines,
FavoriteTimelineResponse,
ResponseTimeline,
SortTimeline,
TimelineResult,
TimelineResponse,
TimelineType,
TimelineStatus,
ResolvedTimelineWithOutcomeSavedObject,
TimelineSavedObject,
ResolvedTimeline,
SavedTimeline,
TimelineWithoutExternalRefs,
SavedTimelineWithSavedObjectId,
} from '../../../../../common/api/timeline';
import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../../common/api/timeline';
import type { SavedObjectTimelineWithoutExternalRefs } from '../../../../../common/types/timeline/saved_object';
@ -49,11 +46,13 @@ import { timelineFieldsMigrator } from './field_migrator';
export { pickSavedTimeline } from './pick_saved_timeline';
export { convertSavedObjectToSavedTimeline } from './convert_saved_object_to_savedtimeline';
type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'savedQueryId'>;
export const getTimeline = async (
request: FrameworkRequest,
timelineId: string,
timelineType: TimelineType | null = TimelineTypeEnum.default
): Promise<TimelineSavedObject> => {
): Promise<TimelineResponse> => {
let timelineIdToUse = timelineId;
try {
if (timelineType === TimelineTypeEnum.template) {
@ -77,7 +76,7 @@ export const getTimeline = async (
export const getTimelineOrNull = async (
frameworkRequest: FrameworkRequest,
savedObjectId: string
): Promise<TimelineSavedObject | null> => {
): Promise<TimelineResponse | null> => {
let timeline = null;
try {
timeline = await getTimeline(frameworkRequest, savedObjectId);
@ -89,23 +88,19 @@ export const getTimelineOrNull = async (
export const resolveTimelineOrNull = async (
frameworkRequest: FrameworkRequest,
savedObjectId: string
): Promise<ResolvedTimelineWithOutcomeSavedObject | null> => {
let resolvedTimeline = null;
): Promise<ResolvedTimeline | null> => {
try {
resolvedTimeline = await resolveSavedTimeline(frameworkRequest, savedObjectId);
// eslint-disable-next-line no-empty
} catch (e) {}
return resolvedTimeline;
// }
const resolvedTimeline = await resolveSavedTimeline(frameworkRequest, savedObjectId);
return resolvedTimeline;
} catch (e) {
return null;
}
};
export const getTimelineByTemplateTimelineId = async (
request: FrameworkRequest,
templateTimelineId: string
): Promise<{
totalCount: number;
timeline: TimelineSavedObject[];
}> => {
): Promise<GetTimelinesResponse> => {
const options: SavedObjectsFindOptions = {
type: timelineSavedObjectType,
filter: `siem-ui-timeline.attributes.templateTimelineId: "${templateTimelineId}"`,
@ -117,7 +112,7 @@ export const getTimelineByTemplateTimelineId = async (
export const getTimelineTemplateOrNull = async (
frameworkRequest: FrameworkRequest,
templateTimelineId: string
): Promise<TimelineSavedObject | null> => {
): Promise<TimelineResponse | null> => {
let templateTimeline = null;
try {
templateTimeline = await getTimelineByTemplateTimelineId(frameworkRequest, templateTimelineId);
@ -190,10 +185,7 @@ export const getExistingPrepackagedTimelines = async (
request: FrameworkRequest,
countsOnly?: boolean,
pageInfo?: PageInfoTimeline
): Promise<{
totalCount: number;
timeline: TimelineSavedObject[];
}> => {
): Promise<GetTimelinesResponse> => {
const queryPageInfo =
countsOnly && pageInfo == null
? {
@ -218,7 +210,7 @@ export const getAllTimeline = async (
sort: SortTimeline | null,
status: TimelineStatus | null,
timelineType: TimelineType | null
): Promise<AllTimelinesResponse> => {
): Promise<GetTimelinesResponse> => {
const searchTerm = search != null ? search : undefined;
const searchFields = ['title', 'description'];
const filter = combineFilters([
@ -291,7 +283,7 @@ export const getAllTimeline = async (
export const getDraftTimeline = async (
request: FrameworkRequest,
timelineType: TimelineType | null
): Promise<ResponseTimelines> => {
): Promise<GetTimelinesResponse> => {
const filter = combineFilters([
getTimelineTypeFilter(timelineType ?? null, TimelineStatusEnum.draft),
getTimelinesCreatedAndUpdatedByCurrentUser({ request }),
@ -385,13 +377,19 @@ export const persistFavorite = async (
}
};
export interface InternalTimelineResponse {
code: number;
message: string;
timeline: TimelineResponse;
}
export const persistTimeline = async (
request: FrameworkRequest,
timelineId: string | null,
version: string | null,
timeline: SavedTimeline,
isImmutable?: boolean
): Promise<ResponseTimeline> => {
): Promise<InternalTimelineResponse> => {
const savedObjectsClient = (await request.context.core).savedObjects.client;
const userInfo = isImmutable ? ({ username: 'Elastic' } as AuthenticatedUser) : request.user;
try {
@ -414,7 +412,7 @@ export const persistTimeline = async (
timeline: await getSavedTimeline(request, timelineId),
};
} else if (getOr(null, 'output.statusCode', err) === 403) {
const timelineToReturn: TimelineResult = {
const timelineToReturn: TimelineResponse = {
...timeline,
savedObjectId: '',
version: '',
@ -439,7 +437,7 @@ export const createTimeline = async ({
timeline: SavedTimeline;
savedObjectsClient: SavedObjectsClientContract;
userInfo: AuthenticatedUser | null;
}) => {
}): Promise<InternalTimelineResponse> => {
const { transformedFields: migratedAttributes, references } =
timelineFieldsMigrator.extractFieldsToReferences<TimelineWithoutExternalRefs>({
data: pickSavedTimeline(timelineId, timeline, userInfo),
@ -479,7 +477,7 @@ const updateTimeline = async ({
savedObjectsClient: SavedObjectsClientContract;
userInfo: AuthenticatedUser | null;
version: string | null;
}) => {
}): Promise<InternalTimelineResponse> => {
const rawTimelineSavedObject =
await savedObjectsClient.get<SavedObjectTimelineWithoutExternalRefs>(
timelineSavedObjectType,
@ -516,11 +514,12 @@ export const updatePartialSavedTimeline = async (
timelineId
);
const { transformedFields, references } =
timelineFieldsMigrator.extractFieldsToReferences<TimelineWithoutExternalRefs>({
data: timeline,
existingReferences: currentSavedTimeline.references,
});
const { transformedFields, references } = timelineFieldsMigrator.extractFieldsToReferences<
Omit<SavedTimelineWithSavedObjectId, 'dataViewId' | 'savedQueryId'>
>({
data: timeline,
existingReferences: currentSavedTimeline.references,
});
const timelineUpdateAttributes = pickSavedTimeline(
null,
@ -588,7 +587,7 @@ export const copyTimeline = async (
request: FrameworkRequest,
timeline: SavedTimeline,
timelineId: string
): Promise<ResponseTimeline> => {
): Promise<InternalTimelineResponse> => {
const savedObjectsClient = (await request.context.core).savedObjects.client;
// Fetch all objects that need to be copied
@ -658,7 +657,10 @@ const resolveBasicSavedTimeline = async (request: FrameworkRequest, timelineId:
};
};
const resolveSavedTimeline = async (request: FrameworkRequest, timelineId: string) => {
const resolveSavedTimeline = async (
request: FrameworkRequest,
timelineId: string
): Promise<ResolvedTimeline> => {
const userName = request.user?.username ?? UNAUTHENTICATED_USER;
const { resolvedTimelineSavedObject, ...resolveAttributes } = await resolveBasicSavedTimeline(
@ -673,7 +675,6 @@ const resolveSavedTimeline = async (request: FrameworkRequest, timelineId: strin
]);
const [notes, pinnedEvents, timeline] = timelineWithNotesAndPinnedEvents;
return {
timeline: timelineWithReduxProperties(notes, pinnedEvents, timeline, userName),
...resolveAttributes,
@ -742,9 +743,9 @@ export const convertStringToBase64 = (text: string): string => Buffer.from(text)
export const timelineWithReduxProperties = (
notes: Note[],
pinnedEvents: PinnedEvent[],
timeline: TimelineSavedObject,
timeline: TimelineResponse,
userName: string
): TimelineSavedObject => ({
): TimelineResponse => ({
...timeline,
favorite:
timeline.favorite != null && userName != null
@ -789,7 +790,7 @@ export const getSelectedTimelines = async (
);
const timelineObjects: {
timelines: TimelineSavedObject[];
timelines: TimelineResponse[];
errors: ExportTimelineNotFoundError[];
} = savedObjects.saved_objects.reduce(
(acc, savedObject) => {
@ -805,7 +806,7 @@ export const getSelectedTimelines = async (
return { errors: [...acc.errors, savedObject.error], timelines: acc.timelines };
},
{
timelines: [] as TimelineSavedObject[],
timelines: [] as TimelineResponse[],
errors: [] as ExportTimelineNotFoundError[],
}
);

View file

@ -9,14 +9,14 @@ import { isEmpty } from 'lodash/fp';
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
import { getUserDisplayName } from '@kbn/user-profile-components';
import { UNAUTHENTICATED_USER } from '../../../../../common/constants';
import type { SavedTimelineWithSavedObjectId } from '../../../../../common/api/timeline';
import type { SavedTimeline } from '../../../../../common/api/timeline';
import { TimelineTypeEnum, TimelineStatusEnum } from '../../../../../common/api/timeline';
export const pickSavedTimeline = (
timelineId: string | null,
savedTimeline: SavedTimelineWithSavedObjectId,
savedTimeline: SavedTimeline,
userInfo: AuthenticatedUser | null
): SavedTimelineWithSavedObjectId => {
): SavedTimeline => {
const dateNow = new Date().valueOf();
if (timelineId == null) {

View file

@ -7,9 +7,9 @@
import path, { join, resolve } from 'path';
import type {
CheckTimelineStatusRt,
TimelineSavedObject,
ImportTimelinesSchema,
TimelineResponse,
ImportTimelines,
InstallPrepackedTimelinesRequestBody,
} from '../../../../common/api/timeline';
import type { FrameworkRequest } from '../../framework';
@ -19,9 +19,9 @@ import { getExistingPrepackagedTimelines } from '../saved_object/timelines';
import { loadData, getReadables } from './common';
export const getTimelinesToUpdate = (
timelinesFromFileSystem: ImportTimelinesSchema[],
installedTimelines: TimelineSavedObject[]
): ImportTimelinesSchema[] => {
timelinesFromFileSystem: ImportTimelines[],
installedTimelines: TimelineResponse[]
): ImportTimelines[] => {
return timelinesFromFileSystem.filter((timeline) =>
installedTimelines.some((installedTimeline) => {
return (
@ -34,9 +34,9 @@ export const getTimelinesToUpdate = (
};
export const getTimelinesToInstall = (
timelinesFromFileSystem: ImportTimelinesSchema[],
installedTimelines: TimelineSavedObject[]
): ImportTimelinesSchema[] => {
timelinesFromFileSystem: ImportTimelines[],
installedTimelines: TimelineResponse[]
): ImportTimelines[] => {
return timelinesFromFileSystem.filter(
(timeline) =>
!installedTimelines.some(
@ -49,11 +49,11 @@ export const checkTimelinesStatus = async (
frameworkRequest: FrameworkRequest,
filePath?: string,
fileName?: string
): Promise<CheckTimelineStatusRt | Error> => {
): Promise<InstallPrepackedTimelinesRequestBody | Error> => {
let readStream;
let timeline: {
totalCount: number;
timeline: TimelineSavedObject[];
timeline: TimelineResponse[];
};
const dir = resolve(
join(
@ -75,7 +75,7 @@ export const checkTimelinesStatus = async (
};
}
return loadData<'utf-8', CheckTimelineStatusRt>(
return loadData<'utf-8', InstallPrepackedTimelinesRequestBody>(
readStream,
<T>(timelinesFromFileSystem: T) => {
if (Array.isArray(timelinesFromFileSystem)) {

View file

@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type * as rt from 'io-ts';
import { set } from '@kbn/safer-lodash-set/fp';
import readline from 'readline';
import fs from 'fs';
@ -13,7 +13,6 @@ import { createListStream } from '@kbn/utils';
import { schema } from '@kbn/config-schema';
import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server';
import { formatErrors } from '@kbn/securitysolution-io-ts-utils';
import type { FrameworkRequest } from '../../framework';
@ -38,12 +37,6 @@ export const buildFrameworkRequest = async (
export const escapeHatch = schema.object({}, { unknowns: 'allow' });
type ErrorFactory = (message: string) => Error;
export const throwErrors = (createError: ErrorFactory) => (errors: rt.Errors) => {
throw createError(formatErrors(errors).join('\n'));
};
export const getReadables = (dataPath: string): Promise<Readable> =>
new Promise((resolved, reject) => {
const contents: string[] = [];

View file

@ -26,7 +26,7 @@ import {
NOT_ALLOW_UPDATE_STATUS_ERROR_MESSAGE,
TEMPLATE_TIMELINE_VERSION_CONFLICT_MESSAGE,
} from './failure_cases';
import type { TimelineSavedObject } from '../../../../common/api/timeline';
import type { TimelineResponse } from '../../../../common/api/timeline';
import { TimelineStatusEnum, TimelineTypeEnum } from '../../../../common/api/timeline';
import { mockGetTimelineValue, mockGetTemplateTimelineValue } from '../__mocks__/import_timelines';
@ -69,7 +69,7 @@ describe('failure cases', () => {
const version = null;
const templateTimelineVersion = null;
const templateTimelineId = null;
const existTimeline = mockGetTimelineValue as TimelineSavedObject;
const existTimeline = mockGetTimelineValue as TimelineResponse;
const existTemplateTimeline = null;
const result = checkIsCreateFailureCases(
isHandlingTemplateTimeline,
@ -94,7 +94,7 @@ describe('failure cases', () => {
const templateTimelineVersion = 1;
const templateTimelineId = 'template-timeline-id-one';
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsCreateFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -144,7 +144,7 @@ describe('failure cases', () => {
const templateTimelineVersion = null;
const templateTimelineId = null;
const existTimeline = {
...(mockGetTimelineValue as TimelineSavedObject),
...(mockGetTimelineValue as TimelineResponse),
status: TimelineStatusEnum.immutable,
};
const existTemplateTimeline = null;
@ -172,7 +172,7 @@ describe('failure cases', () => {
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = {
...(mockGetTemplateTimelineValue as TimelineSavedObject),
...(mockGetTemplateTimelineValue as TimelineResponse),
status: TimelineStatusEnum.immutable,
};
const result = checkIsUpdateFailureCases(
@ -198,7 +198,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -246,10 +246,10 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = {
...(mockGetTemplateTimelineValue as TimelineSavedObject),
...(mockGetTemplateTimelineValue as TimelineResponse),
savedObjectId: 'someOtherId',
};
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -273,7 +273,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -297,7 +297,7 @@ describe('failure cases', () => {
const templateTimelineVersion = null;
const templateTimelineId = null;
const existTimeline = {
...(mockGetTemplateTimelineValue as TimelineSavedObject),
...(mockGetTemplateTimelineValue as TimelineResponse),
savedObjectId: 'someOtherId',
};
const existTemplateTimeline = null;
@ -326,7 +326,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsCreateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.draft,
@ -350,7 +350,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsCreateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -373,7 +373,7 @@ describe('failure cases', () => {
const version = mockGetTimelineValue.version;
const templateTimelineVersion = null;
const templateTimelineId = null;
const existTimeline = mockGetTimelineValue as TimelineSavedObject;
const existTimeline = mockGetTimelineValue as TimelineResponse;
const existTemplateTimeline = null;
const result = checkIsCreateViaImportFailureCases(
isHandlingTemplateTimeline,
@ -399,7 +399,7 @@ describe('failure cases', () => {
const version = mockGetTimelineValue.version;
const templateTimelineVersion = null;
const templateTimelineId = null;
const existTimeline = mockGetTimelineValue as TimelineSavedObject;
const existTimeline = mockGetTimelineValue as TimelineResponse;
const existTemplateTimeline = null;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
@ -424,7 +424,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -448,7 +448,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.immutable,
@ -496,10 +496,10 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = {
...(mockGetTemplateTimelineValue as TimelineSavedObject),
...(mockGetTemplateTimelineValue as TimelineResponse),
savedObjectId: 'someOtherId',
};
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -523,7 +523,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,
@ -547,7 +547,7 @@ describe('failure cases', () => {
const templateTimelineVersion = mockGetTemplateTimelineValue.templateTimelineVersion;
const templateTimelineId = mockGetTemplateTimelineValue.templateTimelineId;
const existTimeline = null;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineSavedObject;
const existTemplateTimeline = mockGetTemplateTimelineValue as TimelineResponse;
const result = checkIsUpdateViaImportFailureCases(
isHandlingTemplateTimeline,
TimelineStatusEnum.active,

View file

@ -6,7 +6,7 @@
*/
import { isEmpty } from 'lodash/fp';
import type { TimelineType, TimelineSavedObject } from '../../../../common/api/timeline';
import type { TimelineType, TimelineResponse } from '../../../../common/api/timeline';
import { type TimelineStatus, TimelineStatusEnum } from '../../../../common/api/timeline';
export const UPDATE_TIMELINE_ERROR_MESSAGE =
@ -42,8 +42,8 @@ export const DEFAULT_ERROR = `Something has gone wrong. We didn't handle somethi
const isUpdatingStatus = (
isHandlingTemplateTimeline: boolean,
status: TimelineStatus | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
const obj = isHandlingTemplateTimeline ? existTemplateTimeline : existTimeline;
return obj?.status === TimelineStatusEnum.immutable ? UPDATE_STATUS_ERROR_MESSAGE : null;
@ -76,8 +76,8 @@ const commonUpdateTemplateTimelineCheck = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (isHandlingTemplateTimeline) {
if (
@ -129,8 +129,8 @@ const commonUpdateTimelineCheck = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (existTimeline == null) {
// timeline !exists
@ -158,8 +158,8 @@ const commonUpdateCases = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (isHandlingTemplateTimeline) {
return commonUpdateTemplateTimelineCheck(
@ -193,8 +193,8 @@ const createTemplateTimelineCheck = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (isHandlingTemplateTimeline && existTemplateTimeline != null) {
// Throw error to create timeline template in patch
@ -219,8 +219,8 @@ export const checkIsUpdateViaImportFailureCases = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (!isHandlingTemplateTimeline) {
if (existTimeline == null) {
@ -281,8 +281,8 @@ export const checkIsUpdateFailureCases = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
const error = isUpdatingStatus(
isHandlingTemplateTimeline,
@ -315,8 +315,8 @@ export const checkIsCreateFailureCases = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (!isHandlingTemplateTimeline && existTimeline != null) {
return {
@ -346,8 +346,8 @@ export const checkIsCreateViaImportFailureCases = (
version: string | null,
templateTimelineVersion: number | null,
templateTimelineId: string | null | undefined,
existTimeline: TimelineSavedObject | null,
existTemplateTimeline: TimelineSavedObject | null
existTimeline: TimelineResponse | null,
existTemplateTimeline: TimelineResponse | null
) => {
if (status === TimelineStatusEnum.draft) {
return {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { TimelineSavedObject } from '../../../../common/api/timeline';
import type { TimelineResponse } from '../../../../common/api/timeline';
import {
type TimelineType,
TimelineTypeEnum,
@ -27,7 +27,7 @@ export class TimelineObject {
public readonly version: string | number | null;
private frameworkRequest: FrameworkRequest;
public data: TimelineSavedObject | null;
public data: TimelineResponse | null;
constructor({
id = null,

Some files were not shown because too many files have changed in this diff Show more