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

## Summary

fixes: https://github.com/elastic/security-team/issues/10235
fixes: https://github.com/elastic/security-team/issues/10237

This is the final PR for migrating over all timeline-related schemas and
types to the new generated zod schemas from our OpenAPI specs. (see
https://github.com/elastic/security-team/issues/10110)
On top of moving to the new schemas/types, this PR also cleans up usage
of now outdated types.

I'm aware of the size of this PR but rest assured, the changes are easy
to review and for most teams, only a handful of files need to be
reviewed:

```markdown
### elastic/security-defend-workflows

* x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_solution_integrations.ts

### elastic/security-detection-rule-management

* 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
* x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/perform_timelines_installation.ts

### elastic/security-detections-response

* x-pack/test/security_solution_cypress/cypress/objects/timeline.ts

### elastic/security-engineering-productivity

* x-pack/test/security_solution_cypress/cypress/objects/timeline.ts
* x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts
```


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Jan Monschke 2024-09-30 20:55:23 +02:00 committed by GitHub
parent 7fd3317423
commit 00789609ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
107 changed files with 2042 additions and 2689 deletions

1
.github/CODEOWNERS vendored
View file

@ -1532,6 +1532,7 @@ x-pack/test/security_solution_api_integration/test_suites/sources @elastic/secur
x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout @elastic/security-threat-hunting-investigations x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout @elastic/security-threat-hunting-investigations
x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/security-threat-hunting-investigations x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/common/timelines @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/common/components/alerts_viewer @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/alerts_viewer @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_action @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_action @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/common/components/event_details @elastic/security-threat-hunting-investigations /x-pack/plugins/security_solution/public/common/components/event_details @elastic/security-threat-hunting-investigations

View file

@ -16356,19 +16356,21 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: >- type: object
#/components/schemas/Security_Timeline_API_TimelineResponse properties:
nullable: true getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- getOneTimeline
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: Indicates that the (template) Timeline was found and returned. description: Indicates that the (template) Timeline was found and returned.
summary: Get Timeline or Timeline template details summary: Get Timeline or Timeline template details
tags: tags:
@ -16405,23 +16407,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -16483,21 +16470,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- persistTimeline
required:
- data
description: Indicates the Timeline was successfully created. description: Indicates the Timeline was successfully created.
'405': '405':
content: content:
@ -16514,6 +16488,37 @@ paths:
tags: tags:
- Security Timeline API - Security Timeline API
- access:securitySolution - 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: /api/timeline/_draft:
get: get:
description: >- description: >-
@ -16532,23 +16537,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: Indicates that the draft Timeline was successfully retrieved. description: Indicates that the draft Timeline was successfully retrieved.
'403': '403':
content: content:
@ -16610,23 +16600,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -16782,28 +16757,14 @@ paths:
schema: schema:
type: object type: object
properties: properties:
file: file: {}
allOf: isImmutable:
- $ref: '#/components/schemas/Security_Timeline_API_Readable' enum:
- type: object - 'true'
properties: - 'false'
hapi: type: string
type: object required:
properties: - file
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
description: The Timelines to import as a readable stream. description: The Timelines to import as a readable stream.
required: true required: true
responses: responses:
@ -16811,13 +16772,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_ImportTimelineResult
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
description: Indicates the import of Timelines was successful. description: Indicates the import of Timelines was successful.
'400': '400':
content: content:
@ -16876,7 +16832,9 @@ paths:
properties: properties:
prepackagedTimelines: prepackagedTimelines:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline' $ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
nullable: true
type: array type: array
timelinesToInstall: timelinesToInstall:
items: items:
@ -16899,13 +16857,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_ImportTimelineResult
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
description: Indicates the installation of prepackaged Timelines was successful. description: Indicates the installation of prepackaged Timelines was successful.
'500': '500':
content: content:
@ -16943,19 +16896,16 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: >- $ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse #/components/schemas/Security_Timeline_API_ResolvedTimeline
nullable: true
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: The (template) Timeline has been found description: The (template) Timeline has been found
'400': '400':
description: The request is missing parameters description: The request is missing parameters
@ -17024,36 +16974,26 @@ paths:
schema: schema:
type: object type: object
properties: properties:
data: customTemplateTimelineCount:
type: object type: number
properties: defaultTimelineCount:
customTemplateTimelineCount: type: number
type: number elasticTemplateTimelineCount:
defaultTimelineCount: type: number
type: number favoriteCount:
elasticTemplateTimelineCount: type: number
type: number templateTimelineCount:
favoriteCount: type: number
type: number timeline:
templateTimelineCount: items:
type: number $ref: >-
timelines: #/components/schemas/Security_Timeline_API_TimelineResponse
items: type: array
$ref: >- totalCount:
#/components/schemas/Security_Timeline_API_TimelineResponse type: number
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
required: required:
- data - timeline
- totalCount
description: Indicates that the (template) Timelines were found and returned. description: Indicates that the (template) Timelines were found and returned.
'400': '400':
content: content:
@ -31335,30 +31275,39 @@ components:
type: object type: object
properties: properties:
aggregatable: aggregatable:
nullable: true
type: boolean type: boolean
category: category:
nullable: true
type: string type: string
columnHeaderType: columnHeaderType:
nullable: true
type: string type: string
description: description:
nullable: true
type: string type: string
example: example:
oneOf: nullable: true
- type: string type: string
- type: number
id: id:
nullable: true
type: string type: string
indexes: indexes:
items: items:
type: string type: string
nullable: true
type: array type: array
name: name:
nullable: true
type: string type: string
placeholder: placeholder:
nullable: true
type: string type: string
searchable: searchable:
nullable: true
type: boolean type: boolean
type: type:
nullable: true
type: string type: string
Security_Timeline_API_DataProviderQueryMatch: Security_Timeline_API_DataProviderQueryMatch:
type: object type: object
@ -31380,6 +31329,10 @@ components:
type: string type: string
queryMatch: queryMatch:
$ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult' $ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/Security_Timeline_API_DataProviderType'
nullable: true
Security_Timeline_API_DataProviderResult: Security_Timeline_API_DataProviderResult:
type: object type: object
properties: properties:
@ -31467,41 +31420,59 @@ components:
type: object type: object
properties: properties:
exists: exists:
type: boolean nullable: true
type: string
match_all: match_all:
nullable: true
type: string type: string
meta: meta:
nullable: true
type: object type: object
properties: properties:
alias: alias:
nullable: true
type: string type: string
controlledBy: controlledBy:
nullable: true
type: string type: string
disabled: disabled:
nullable: true
type: boolean type: boolean
field: field:
nullable: true
type: string type: string
formattedValue: formattedValue:
nullable: true
type: string type: string
index: index:
nullable: true
type: string type: string
key: key:
nullable: true
type: string type: string
negate: negate:
nullable: true
type: boolean type: boolean
params: params:
nullable: true
type: string type: string
type: type:
nullable: true
type: string type: string
value: value:
nullable: true
type: string type: string
missing: missing:
nullable: true
type: string type: string
query: query:
nullable: true
type: string type: string
range: range:
nullable: true
type: string type: string
script: script:
nullable: true
type: string type: string
Security_Timeline_API_GetNotesResult: Security_Timeline_API_GetNotesResult:
type: object type: object
@ -31566,6 +31537,12 @@ components:
version: version:
nullable: true nullable: true
type: string type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Security_Timeline_API_Note: Security_Timeline_API_Note:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BareNote' - $ref: '#/components/schemas/Security_Timeline_API_BareNote'
@ -31586,6 +31563,23 @@ components:
#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody #/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody
- nullable: true - nullable: true
type: object 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: Security_Timeline_API_PinnedEvent:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent' - $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent'
@ -31623,34 +31617,29 @@ components:
nullable: true nullable: true
type: string type: string
value: value:
nullable: true oneOf:
type: string - nullable: true
Security_Timeline_API_Readable: type: string
- items:
type: string
nullable: true
type: array
Security_Timeline_API_ResolvedTimeline:
type: object type: object
properties: properties:
_data: alias_purpose:
additionalProperties: true $ref: >-
type: object #/components/schemas/Security_Timeline_API_SavedObjectResolveAliasPurpose
_encoding: alias_target_id:
type: string type: string
_events: outcome:
additionalProperties: true $ref: '#/components/schemas/Security_Timeline_API_SavedObjectResolveOutcome'
type: object timeline:
_eventsCount: $ref: >-
type: number #/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
_maxListeners: required:
additionalProperties: true - timeline
type: object - outcome
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
Security_Timeline_API_ResponseNote: Security_Timeline_API_ResponseNote:
type: object type: object
properties: properties:
@ -31685,6 +31674,17 @@ components:
- threat_match - threat_match
- zeek - zeek
type: string 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: Security_Timeline_API_SavedTimeline:
type: object type: object
properties: properties:
@ -31713,12 +31713,16 @@ components:
properties: properties:
end: end:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
start: start:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
description: description:
nullable: true nullable: true
type: string type: string
@ -31808,6 +31812,18 @@ components:
updatedBy: updatedBy:
nullable: true nullable: true
type: string 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: Security_Timeline_API_SerializedFilterQueryResult:
type: object type: object
properties: properties:
@ -31857,27 +31873,64 @@ components:
Security_Timeline_API_TimelineResponse: Security_Timeline_API_TimelineResponse:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline' - $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- $ref: >-
#/components/schemas/Security_Timeline_API_SavedTimelineWithSavedObjectId
- type: object - type: object
properties: properties:
eventIdToNoteIds: eventIdToNoteIds:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_Note' $ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array type: array
noteIds: noteIds:
items: items:
type: string type: string
nullable: true
type: array type: array
notes: notes:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_Note' $ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array type: array
pinnedEventIds: pinnedEventIds:
items: items:
type: string type: string
nullable: true
type: array type: array
pinnedEventsSaveObject: pinnedEventsSaveObject:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' $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 type: array
savedObjectId: savedObjectId:
type: string type: string

View file

@ -20445,19 +20445,21 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: >- type: object
#/components/schemas/Security_Timeline_API_TimelineResponse properties:
nullable: true getOneTimeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- getOneTimeline
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: Indicates that the (template) Timeline was found and returned. description: Indicates that the (template) Timeline was found and returned.
summary: Get Timeline or Timeline template details summary: Get Timeline or Timeline template details
tags: tags:
@ -20494,23 +20496,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -20572,21 +20559,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- persistTimeline
required:
- data
description: Indicates the Timeline was successfully created. description: Indicates the Timeline was successfully created.
'405': '405':
content: content:
@ -20603,6 +20577,37 @@ paths:
tags: tags:
- Security Timeline API - Security Timeline API
- access:securitySolution - 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: /api/timeline/_draft:
get: get:
description: >- description: >-
@ -20621,23 +20626,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: Indicates that the draft Timeline was successfully retrieved. description: Indicates that the draft Timeline was successfully retrieved.
'403': '403':
content: content:
@ -20699,23 +20689,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_PersistTimelineResponse
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -20871,28 +20846,14 @@ paths:
schema: schema:
type: object type: object
properties: properties:
file: file: {}
allOf: isImmutable:
- $ref: '#/components/schemas/Security_Timeline_API_Readable' enum:
- type: object - 'true'
properties: - 'false'
hapi: type: string
type: object required:
properties: - file
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
description: The Timelines to import as a readable stream. description: The Timelines to import as a readable stream.
required: true required: true
responses: responses:
@ -20900,13 +20861,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_ImportTimelineResult
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
description: Indicates the import of Timelines was successful. description: Indicates the import of Timelines was successful.
'400': '400':
content: content:
@ -20965,7 +20921,9 @@ paths:
properties: properties:
prepackagedTimelines: prepackagedTimelines:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_SavedTimeline' $ref: >-
#/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
nullable: true
type: array type: array
timelinesToInstall: timelinesToInstall:
items: items:
@ -20988,13 +20946,8 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object $ref: >-
properties: #/components/schemas/Security_Timeline_API_ImportTimelineResult
data:
$ref: >-
#/components/schemas/Security_Timeline_API_ImportTimelineResult
required:
- data
description: Indicates the installation of prepackaged Timelines was successful. description: Indicates the installation of prepackaged Timelines was successful.
'500': '500':
content: content:
@ -21032,19 +20985,16 @@ paths:
content: content:
application/json; Elastic-Api-Version=2023-10-31: application/json; Elastic-Api-Version=2023-10-31:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: >- $ref: >-
#/components/schemas/Security_Timeline_API_TimelineResponse #/components/schemas/Security_Timeline_API_ResolvedTimeline
nullable: true
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: The (template) Timeline has been found description: The (template) Timeline has been found
'400': '400':
description: The request is missing parameters description: The request is missing parameters
@ -21113,36 +21063,26 @@ paths:
schema: schema:
type: object type: object
properties: properties:
data: customTemplateTimelineCount:
type: object type: number
properties: defaultTimelineCount:
customTemplateTimelineCount: type: number
type: number elasticTemplateTimelineCount:
defaultTimelineCount: type: number
type: number favoriteCount:
elasticTemplateTimelineCount: type: number
type: number templateTimelineCount:
favoriteCount: type: number
type: number timeline:
templateTimelineCount: items:
type: number $ref: >-
timelines: #/components/schemas/Security_Timeline_API_TimelineResponse
items: type: array
$ref: >- totalCount:
#/components/schemas/Security_Timeline_API_TimelineResponse type: number
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
required: required:
- data - timeline
- totalCount
description: Indicates that the (template) Timelines were found and returned. description: Indicates that the (template) Timelines were found and returned.
'400': '400':
content: content:
@ -39344,30 +39284,39 @@ components:
type: object type: object
properties: properties:
aggregatable: aggregatable:
nullable: true
type: boolean type: boolean
category: category:
nullable: true
type: string type: string
columnHeaderType: columnHeaderType:
nullable: true
type: string type: string
description: description:
nullable: true
type: string type: string
example: example:
oneOf: nullable: true
- type: string type: string
- type: number
id: id:
nullable: true
type: string type: string
indexes: indexes:
items: items:
type: string type: string
nullable: true
type: array type: array
name: name:
nullable: true
type: string type: string
placeholder: placeholder:
nullable: true
type: string type: string
searchable: searchable:
nullable: true
type: boolean type: boolean
type: type:
nullable: true
type: string type: string
Security_Timeline_API_DataProviderQueryMatch: Security_Timeline_API_DataProviderQueryMatch:
type: object type: object
@ -39389,6 +39338,10 @@ components:
type: string type: string
queryMatch: queryMatch:
$ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult' $ref: '#/components/schemas/Security_Timeline_API_QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/Security_Timeline_API_DataProviderType'
nullable: true
Security_Timeline_API_DataProviderResult: Security_Timeline_API_DataProviderResult:
type: object type: object
properties: properties:
@ -39476,41 +39429,59 @@ components:
type: object type: object
properties: properties:
exists: exists:
type: boolean nullable: true
type: string
match_all: match_all:
nullable: true
type: string type: string
meta: meta:
nullable: true
type: object type: object
properties: properties:
alias: alias:
nullable: true
type: string type: string
controlledBy: controlledBy:
nullable: true
type: string type: string
disabled: disabled:
nullable: true
type: boolean type: boolean
field: field:
nullable: true
type: string type: string
formattedValue: formattedValue:
nullable: true
type: string type: string
index: index:
nullable: true
type: string type: string
key: key:
nullable: true
type: string type: string
negate: negate:
nullable: true
type: boolean type: boolean
params: params:
nullable: true
type: string type: string
type: type:
nullable: true
type: string type: string
value: value:
nullable: true
type: string type: string
missing: missing:
nullable: true
type: string type: string
query: query:
nullable: true
type: string type: string
range: range:
nullable: true
type: string type: string
script: script:
nullable: true
type: string type: string
Security_Timeline_API_GetNotesResult: Security_Timeline_API_GetNotesResult:
type: object type: object
@ -39575,6 +39546,12 @@ components:
version: version:
nullable: true nullable: true
type: string type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Security_Timeline_API_Note: Security_Timeline_API_Note:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BareNote' - $ref: '#/components/schemas/Security_Timeline_API_BareNote'
@ -39595,6 +39572,23 @@ components:
#/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody #/components/schemas/Security_Timeline_API_PinnedEventBaseResponseBody
- nullable: true - nullable: true
type: object 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: Security_Timeline_API_PinnedEvent:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent' - $ref: '#/components/schemas/Security_Timeline_API_BarePinnedEvent'
@ -39632,34 +39626,29 @@ components:
nullable: true nullable: true
type: string type: string
value: value:
nullable: true oneOf:
type: string - nullable: true
Security_Timeline_API_Readable: type: string
- items:
type: string
nullable: true
type: array
Security_Timeline_API_ResolvedTimeline:
type: object type: object
properties: properties:
_data: alias_purpose:
additionalProperties: true $ref: >-
type: object #/components/schemas/Security_Timeline_API_SavedObjectResolveAliasPurpose
_encoding: alias_target_id:
type: string type: string
_events: outcome:
additionalProperties: true $ref: '#/components/schemas/Security_Timeline_API_SavedObjectResolveOutcome'
type: object timeline:
_eventsCount: $ref: >-
type: number #/components/schemas/Security_Timeline_API_TimelineSavedToReturnObject
_maxListeners: required:
additionalProperties: true - timeline
type: object - outcome
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
Security_Timeline_API_ResponseNote: Security_Timeline_API_ResponseNote:
type: object type: object
properties: properties:
@ -39694,6 +39683,17 @@ components:
- threat_match - threat_match
- zeek - zeek
type: string 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: Security_Timeline_API_SavedTimeline:
type: object type: object
properties: properties:
@ -39722,12 +39722,16 @@ components:
properties: properties:
end: end:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
start: start:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
description: description:
nullable: true nullable: true
type: string type: string
@ -39817,6 +39821,18 @@ components:
updatedBy: updatedBy:
nullable: true nullable: true
type: string 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: Security_Timeline_API_SerializedFilterQueryResult:
type: object type: object
properties: properties:
@ -39866,27 +39882,64 @@ components:
Security_Timeline_API_TimelineResponse: Security_Timeline_API_TimelineResponse:
allOf: allOf:
- $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline' - $ref: '#/components/schemas/Security_Timeline_API_SavedTimeline'
- $ref: >-
#/components/schemas/Security_Timeline_API_SavedTimelineWithSavedObjectId
- type: object - type: object
properties: properties:
eventIdToNoteIds: eventIdToNoteIds:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_Note' $ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array type: array
noteIds: noteIds:
items: items:
type: string type: string
nullable: true
type: array type: array
notes: notes:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_Note' $ref: '#/components/schemas/Security_Timeline_API_Note'
nullable: true
type: array type: array
pinnedEventIds: pinnedEventIds:
items: items:
type: string type: string
nullable: true
type: array type: array
pinnedEventsSaveObject: pinnedEventsSaveObject:
items: items:
$ref: '#/components/schemas/Security_Timeline_API_PinnedEvent' $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 type: array
savedObjectId: savedObjectId:
type: string type: string

View file

@ -296,6 +296,10 @@ import type {
CleanDraftTimelinesRequestBodyInput, CleanDraftTimelinesRequestBodyInput,
CleanDraftTimelinesResponse, CleanDraftTimelinesResponse,
} from './timeline/clean_draft_timelines/clean_draft_timelines_route.gen'; } from './timeline/clean_draft_timelines/clean_draft_timelines_route.gen';
import type {
CopyTimelineRequestBodyInput,
CopyTimelineResponse,
} from './timeline/copy_timeline/copy_timeline_route.gen';
import type { import type {
CreateTimelinesRequestBodyInput, CreateTimelinesRequestBodyInput,
CreateTimelinesResponse, CreateTimelinesResponse,
@ -556,6 +560,23 @@ If asset criticality records already exist for the specified entities, those rec
}) })
.catch(catchAxiosErrorFormatAndThrow); .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() { async createAlertsIndex() {
this.log.info(`${new Date().toISOString()} Calling API CreateAlertsIndex`); this.log.info(`${new Date().toISOString()} Calling API CreateAlertsIndex`);
return this.kbnClient return this.kbnClient
@ -1993,6 +2014,9 @@ export interface BulkUpsertAssetCriticalityRecordsProps {
export interface CleanDraftTimelinesProps { export interface CleanDraftTimelinesProps {
body: CleanDraftTimelinesRequestBodyInput; body: CleanDraftTimelinesRequestBodyInput;
} }
export interface CopyTimelineProps {
body: CopyTimelineRequestBodyInput;
}
export interface CreateAlertsMigrationProps { export interface CreateAlertsMigrationProps {
body: CreateAlertsMigrationRequestBodyInput; body: CreateAlertsMigrationRequestBodyInput;
} }

View file

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

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info: info:
title: Elastic Security - Timeline - Draft Timeline API title: Elastic Security - Timeline - Draft Timeline API
version: '2023-10-31' version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timeline/_draft: /api/timeline/_draft:
post: post:
@ -39,19 +32,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
'403': '403':
description: Indicates that the user does not have the required permissions to create a draft Timeline. description: Indicates that the user does not have the required permissions to create a draft Timeline.
content: 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 { z } from '@kbn/zod';
import { import {
SavedTimeline,
TimelineStatus, TimelineStatus,
TimelineType, TimelineType,
SavedTimeline, PersistTimelineResponse,
TimelineResponse,
} from '../model/components.gen'; } from '../model/components.gen';
export type CreateTimelinesRequestBody = z.infer<typeof CreateTimelinesRequestBody>; export type CreateTimelinesRequestBody = z.infer<typeof CreateTimelinesRequestBody>;
export const CreateTimelinesRequestBody = z.object({ export const CreateTimelinesRequestBody = z.object({
timeline: SavedTimeline,
status: TimelineStatus.nullable().optional(), status: TimelineStatus.nullable().optional(),
timelineId: z.string().nullable().optional(), timelineId: z.string().nullable().optional(),
templateTimelineId: z.string().nullable().optional(), templateTimelineId: z.string().nullable().optional(),
templateTimelineVersion: z.number().nullable().optional(), templateTimelineVersion: z.number().nullable().optional(),
timelineType: TimelineType.nullable().optional(), timelineType: TimelineType.nullable().optional(),
version: z.string().nullable().optional(), version: z.string().nullable().optional(),
timeline: SavedTimeline,
}); });
export type CreateTimelinesRequestBodyInput = z.input<typeof CreateTimelinesRequestBody>; export type CreateTimelinesRequestBodyInput = z.input<typeof CreateTimelinesRequestBody>;
export type CreateTimelinesResponse = z.infer<typeof CreateTimelinesResponse>; export type CreateTimelinesResponse = z.infer<typeof CreateTimelinesResponse>;
export const CreateTimelinesResponse = z.object({ export const CreateTimelinesResponse = PersistTimelineResponse;
data: z.object({
persistTimeline: z.object({
timeline: TimelineResponse.optional(),
}),
}),
});

View file

@ -5,13 +5,6 @@ info:
externalDocs: externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-create.html url: https://www.elastic.co/guide/en/security/current/timeline-api-create.html
description: Documentation description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timeline: /api/timeline:
post: post:
@ -29,9 +22,10 @@ paths:
application/json: application/json:
schema: schema:
type: object type: object
required: required: [timeline]
- timeline
properties: properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
status: status:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineStatus' $ref: '../model/components.schema.yaml#/components/schemas/TimelineStatus'
nullable: true nullable: true
@ -50,26 +44,13 @@ paths:
version: version:
type: string type: string
nullable: true nullable: true
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/SavedTimeline'
responses: responses:
'200': '200':
description: Indicates the Timeline was successfully created. description: Indicates the Timeline was successfully created.
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
'405': '405':
description: Indicates that there was an error in the Timeline creation. description: Indicates that there was an error in the Timeline creation.
content: 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: info:
title: Elastic Security - Timeline - Notes API title: Elastic Security - Timeline - Notes API
version: '2023-10-31' version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/note: /api/note:
delete: delete:

View file

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

View file

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

View file

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

View file

@ -2,13 +2,6 @@ openapi: 3.0.0
info: info:
title: Elastic Security - Timeline - Get Draft Timelines API title: Elastic Security - Timeline - Get Draft Timelines API
version: '2023-10-31' version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timeline/_draft: /api/timeline/_draft:
get: get:
@ -31,19 +24,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '../model/components.schema.yaml#/components/schemas/PersistTimelineResponse'
required: [data]
properties:
data:
type: object
required: [persistTimeline]
properties:
persistTimeline:
type: object
required: [timeline]
properties:
timeline:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
'403': '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. 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: 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: info:
title: Elastic Security - Timeline - Notes API title: Elastic Security - Timeline - Notes API
version: '2023-10-31' version: '2023-10-31'
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/note: /api/note:
get: get:

View file

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

View file

@ -5,13 +5,6 @@ info:
externalDocs: externalDocs:
url: https://www.elastic.co/guide/en/security/current/_get_timeline_or_timeline_template_by_savedobjectid.html url: https://www.elastic.co/guide/en/security/current/_get_timeline_or_timeline_template_by_savedobjectid.html
description: Documentation description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timeline: /api/timeline:
get: get:
@ -39,13 +32,15 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object oneOf:
required: [data] - type: object
properties: required: [data]
data:
type: object
required: [getOneTimeline]
properties: properties:
getOneTimeline: data:
$ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse' type: object
nullable: true 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 type GetTimelinesResponse = z.infer<typeof GetTimelinesResponse>;
export const GetTimelinesResponse = z.object({ export const GetTimelinesResponse = z.object({
data: z.object({ timeline: z.array(TimelineResponse),
timelines: z.array(TimelineResponse), totalCount: z.number(),
totalCount: z.number(), defaultTimelineCount: z.number().optional(),
defaultTimelineCount: z.number(), templateTimelineCount: z.number().optional(),
templateTimelineCount: z.number(), favoriteCount: z.number().optional(),
favoriteCount: z.number(), elasticTemplateTimelineCount: z.number().optional(),
elasticTemplateTimelineCount: z.number(), customTemplateTimelineCount: z.number().optional(),
customTemplateTimelineCount: z.number(),
}),
}); });

View file

@ -5,13 +5,6 @@ info:
externalDocs: externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-get.html url: https://www.elastic.co/guide/en/security/current/timeline-api-get.html
description: Documentation description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timelines: /api/timelines:
get: get:
@ -75,37 +68,27 @@ paths:
application/json: application/json:
schema: schema:
type: object type: object
required: [data] required: [
timeline,
totalCount,
]
properties: properties:
data: timeline:
type: object type: array
required: items:
[ $ref: '../model/components.schema.yaml#/components/schemas/TimelineResponse'
timelines, totalCount:
totalCount, type: number
defaultTimelineCount, defaultTimelineCount:
templateTimelineCount, type: number
favoriteCount, templateTimelineCount:
elasticTemplateTimelineCount, type: number
customTemplateTimelineCount, favoriteCount:
] type: number
properties: elasticTemplateTimelineCount:
timelines: type: number
type: array customTemplateTimelineCount:
items: type: number
$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': '400':
description: Bad request. The user supplied invalid data. description: Bad request. The user supplied invalid data.
content: 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 { 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 type ImportTimelinesRequestBody = z.infer<typeof ImportTimelinesRequestBody>;
export const ImportTimelinesRequestBody = z.object({ export const ImportTimelinesRequestBody = z.object({
file: Readable.merge( isImmutable: z.enum(['true', 'false']).optional(),
z.object({ file: z.unknown(),
hapi: z.object({
filename: z.string(),
headers: z.object({}),
isImmutable: z.enum(['true', 'false']).optional(),
}),
})
),
}); });
export type ImportTimelinesRequestBodyInput = z.input<typeof ImportTimelinesRequestBody>; export type ImportTimelinesRequestBodyInput = z.input<typeof ImportTimelinesRequestBody>;
export type ImportTimelinesResponse = z.infer<typeof ImportTimelinesResponse>; export type ImportTimelinesResponse = z.infer<typeof ImportTimelinesResponse>;
export const ImportTimelinesResponse = z.object({ export const ImportTimelinesResponse = ImportTimelineResult;
data: ImportTimelineResult,
});

View file

@ -5,13 +5,6 @@ info:
externalDocs: externalDocs:
url: https://www.elastic.co/guide/en/security/current/timeline-api-import.html url: https://www.elastic.co/guide/en/security/current/timeline-api-import.html
description: Documentation description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/timeline/_import: /api/timeline/_import:
post: post:
@ -29,37 +22,21 @@ paths:
application/json: application/json:
schema: schema:
type: object type: object
required: [file]
properties: properties:
file: isImmutable:
allOf: type: string
- $ref: '../model/components.schema.yaml#/components/schemas/Readable' enum:
- type: object - 'true'
required: [hapi] - 'false'
properties: file: {}
hapi:
type: object
required: [filename, headers]
properties:
filename:
type: string
headers:
type: object
isImmutable:
type: string
enum:
- 'true'
- 'false'
responses: responses:
'200': '200':
description: Indicates the import of Timelines was successful. description: Indicates the import of Timelines was successful.
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
required: [data]
properties:
data:
$ref: '../model/components.schema.yaml#/components/schemas/ImportTimelineResult'
'400': '400':
description: Indicates the import of Timelines was unsuccessful because of an invalid file extension. 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 './model/api';
export * from './routes'; 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 { 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< export type InstallPrepackedTimelinesRequestBody = z.infer<
typeof InstallPrepackedTimelinesRequestBody typeof InstallPrepackedTimelinesRequestBody
@ -24,13 +28,11 @@ export type InstallPrepackedTimelinesRequestBody = z.infer<
export const InstallPrepackedTimelinesRequestBody = z.object({ export const InstallPrepackedTimelinesRequestBody = z.object({
timelinesToInstall: z.array(ImportTimelines.nullable()), timelinesToInstall: z.array(ImportTimelines.nullable()),
timelinesToUpdate: z.array(ImportTimelines.nullable()), timelinesToUpdate: z.array(ImportTimelines.nullable()),
prepackagedTimelines: z.array(SavedTimeline), prepackagedTimelines: z.array(TimelineSavedToReturnObject.nullable()),
}); });
export type InstallPrepackedTimelinesRequestBodyInput = z.input< export type InstallPrepackedTimelinesRequestBodyInput = z.input<
typeof InstallPrepackedTimelinesRequestBody typeof InstallPrepackedTimelinesRequestBody
>; >;
export type InstallPrepackedTimelinesResponse = z.infer<typeof InstallPrepackedTimelinesResponse>; export type InstallPrepackedTimelinesResponse = z.infer<typeof InstallPrepackedTimelinesResponse>;
export const InstallPrepackedTimelinesResponse = z.object({ export const InstallPrepackedTimelinesResponse = ImportTimelineResult;
data: ImportTimelineResult,
});

View file

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

View file

@ -5,30 +5,35 @@
* 2.0. * 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 type { DataProviderType } from './components.gen';
import { import {
BareNote, BareNote,
BarePinnedEvent, BarePinnedEvent,
ColumnHeaderResult,
DataProviderTypeEnum, DataProviderTypeEnum,
DataProviderResult,
FavoriteTimelineResponse, FavoriteTimelineResponse,
type FavoriteTimelineResult, FilterTimelineResult,
ImportTimelineResult,
ImportTimelines,
type Note, type Note,
PinnedEvent, PinnedEvent,
PersistTimelineResponse,
QueryMatchResult,
ResolvedTimeline,
RowRendererId, RowRendererId,
RowRendererIdEnum, RowRendererIdEnum,
SavedTimeline,
SavedTimelineWithSavedObjectId,
Sort,
SortDirection,
SortFieldTimeline, SortFieldTimeline,
SortFieldTimelineEnum, SortFieldTimelineEnum,
TemplateTimelineType, TemplateTimelineType,
TemplateTimelineTypeEnum, TemplateTimelineTypeEnum,
TimelineErrorResponse,
TimelineResponse,
TimelineSavedToReturnObject,
TimelineStatus, TimelineStatus,
TimelineStatusEnum, TimelineStatusEnum,
TimelineType, TimelineType,
@ -38,412 +43,54 @@ import {
export { export {
BareNote, BareNote,
BarePinnedEvent, BarePinnedEvent,
ColumnHeaderResult,
DataProviderResult,
DataProviderType, DataProviderType,
DataProviderTypeEnum, DataProviderTypeEnum,
FavoriteTimelineResponse, FavoriteTimelineResponse,
FilterTimelineResult,
ImportTimelineResult,
ImportTimelines,
Note, Note,
PinnedEvent, PinnedEvent,
PersistTimelineResponse,
QueryMatchResult,
ResolvedTimeline,
RowRendererId, RowRendererId,
RowRendererIdEnum, RowRendererIdEnum,
SavedTimeline,
SavedTimelineWithSavedObjectId,
Sort,
SortDirection,
SortFieldTimeline, SortFieldTimeline,
SortFieldTimelineEnum, SortFieldTimelineEnum,
TemplateTimelineType, TemplateTimelineType,
TimelineErrorResponse,
TimelineResponse,
TemplateTimelineTypeEnum, TemplateTimelineTypeEnum,
TimelineSavedToReturnObject,
TimelineStatus, TimelineStatus,
TimelineStatusEnum, TimelineStatusEnum,
TimelineType, TimelineType,
TimelineTypeEnum, 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 * This type represents a timeline type stored in a saved object that does not include any fields that reference
* other saved objects. * other saved objects.
*/ */
export type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'savedQueryId'>; export type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'savedQueryId'>;
export const TimelineSavedToReturnObjectRuntimeType = runtimeTypes.intersection([ export type BarePinnedEventWithoutExternalRefs = Omit<BarePinnedEvent, 'timelineId'>;
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>;
/** /**
* 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({ export type BareNoteWithoutExternalRefs = Omit<BareNote, 'timelineId'>;
data: runtimeTypes.type({
persistTimeline: runtimeTypes.intersection([
runtimeTypes.partial({
code: unionWithNullType(runtimeTypes.number),
message: unionWithNullType(runtimeTypes.string),
}),
runtimeTypes.type({
timeline: TimelineSavedToReturnObjectRuntimeType,
}),
]),
}),
});
export const TimelineErrorResponseType = runtimeTypes.union([ export const RowRendererCount = Object.keys(RowRendererIdEnum).length;
runtimeTypes.type({ export const RowRendererValues = Object.values(RowRendererId.Values);
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,
});
/** /**
* Import/export timelines * Import/export timelines
@ -457,187 +104,17 @@ export interface ExportedNotes {
globalNotes: ExportedGlobalNotes; globalNotes: ExportedGlobalNotes;
} }
export type ExportedTimelines = SavedTimeline &
ExportedNotes & {
pinnedEventIds: string[];
};
export interface ExportTimelineNotFoundError { export interface ExportTimelineNotFoundError {
statusCode: number; statusCode: number;
message: string; 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 { export interface PageInfoTimeline {
pageIndex: number; pageIndex: number;
pageSize: 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 { export interface SortTimeline {
sortField: SortFieldTimeline; sortField: SortFieldTimeline;
sortOrder: Direction; sortOrder: SortDirection;
}
export interface GetAllTimelineVariables {
pageInfo: PageInfoTimeline;
search?: Maybe<string>;
sort?: Maybe<SortTimeline>;
onlyUserFavorite?: Maybe<boolean>;
timelineType?: Maybe<TimelineType>;
status?: Maybe<TimelineStatus>;
} }

View file

@ -42,24 +42,24 @@ export const TemplateTimelineTypeEnum = TemplateTimelineType.enum;
export type ColumnHeaderResult = z.infer<typeof ColumnHeaderResult>; export type ColumnHeaderResult = z.infer<typeof ColumnHeaderResult>;
export const ColumnHeaderResult = z.object({ export const ColumnHeaderResult = z.object({
aggregatable: z.boolean().optional(), aggregatable: z.boolean().nullable().optional(),
category: z.string().optional(), category: z.string().nullable().optional(),
columnHeaderType: z.string().optional(), columnHeaderType: z.string().nullable().optional(),
description: z.string().optional(), description: z.string().nullable().optional(),
example: z.union([z.string(), z.number()]).optional(), example: z.string().nullable().optional(),
indexes: z.array(z.string()).optional(), indexes: z.array(z.string()).nullable().optional(),
id: z.string().optional(), id: z.string().nullable().optional(),
name: z.string().optional(), name: z.string().nullable().optional(),
placeholder: z.string().optional(), placeholder: z.string().nullable().optional(),
searchable: z.boolean().optional(), searchable: z.boolean().nullable().optional(),
type: z.string().optional(), type: z.string().nullable().optional(),
}); });
export type QueryMatchResult = z.infer<typeof QueryMatchResult>; export type QueryMatchResult = z.infer<typeof QueryMatchResult>;
export const QueryMatchResult = z.object({ export const QueryMatchResult = z.object({
field: z.string().nullable().optional(), field: z.string().nullable().optional(),
displayField: 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(), displayValue: z.string().nullable().optional(),
operator: z.string().nullable().optional(), operator: z.string().nullable().optional(),
}); });
@ -71,7 +71,8 @@ export const DataProviderQueryMatch = z.object({
id: z.string().nullable().optional(), id: z.string().nullable().optional(),
kqlQuery: z.string().nullable().optional(), kqlQuery: z.string().nullable().optional(),
name: 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>; export type DataProviderResult = z.infer<typeof DataProviderResult>;
@ -119,27 +120,28 @@ export const FavoriteTimelineResult = z.object({
export type FilterTimelineResult = z.infer<typeof FilterTimelineResult>; export type FilterTimelineResult = z.infer<typeof FilterTimelineResult>;
export const FilterTimelineResult = z.object({ export const FilterTimelineResult = z.object({
exists: z.boolean().optional(), exists: z.string().nullable().optional(),
meta: z meta: z
.object({ .object({
alias: z.string().optional(), alias: z.string().nullable().optional(),
controlledBy: z.string().optional(), controlledBy: z.string().nullable().optional(),
disabled: z.boolean().optional(), disabled: z.boolean().nullable().optional(),
field: z.string().optional(), field: z.string().nullable().optional(),
formattedValue: z.string().optional(), formattedValue: z.string().nullable().optional(),
index: z.string().optional(), index: z.string().nullable().optional(),
key: z.string().optional(), key: z.string().nullable().optional(),
negate: z.boolean().optional(), negate: z.boolean().nullable().optional(),
params: z.string().optional(), params: z.string().nullable().optional(),
type: z.string().optional(), type: z.string().nullable().optional(),
value: z.string().optional(), value: z.string().nullable().optional(),
}) })
.nullable()
.optional(), .optional(),
match_all: z.string().optional(), match_all: z.string().nullable().optional(),
missing: z.string().optional(), missing: z.string().nullable().optional(),
query: z.string().optional(), query: z.string().nullable().optional(),
range: z.string().optional(), range: z.string().nullable().optional(),
script: z.string().optional(), script: z.string().nullable().optional(),
}); });
export type SerializedFilterQueryResult = z.infer<typeof SerializedFilterQueryResult>; export type SerializedFilterQueryResult = z.infer<typeof SerializedFilterQueryResult>;
@ -178,8 +180,8 @@ export const SavedTimeline = z.object({
dataViewId: z.string().nullable().optional(), dataViewId: z.string().nullable().optional(),
dateRange: z dateRange: z
.object({ .object({
end: z.union([z.string(), z.number()]).optional(), end: z.union([z.string().nullable(), z.number().nullable()]).optional(),
start: z.union([z.string(), z.number()]).optional(), start: z.union([z.string().nullable(), z.number().nullable()]).optional(),
}) })
.nullable() .nullable()
.optional(), .optional(),
@ -213,6 +215,14 @@ export const SavedTimeline = z.object({
updatedBy: z.string().nullable().optional(), 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 type BareNote = z.infer<typeof BareNote>;
export const BareNote = z.object({ export const BareNote = z.object({
eventId: z.string().nullable().optional(), eventId: z.string().nullable().optional(),
@ -251,18 +261,50 @@ export const PinnedEvent = BarePinnedEvent.merge(
); );
export type TimelineResponse = z.infer<typeof TimelineResponse>; export type TimelineResponse = z.infer<typeof TimelineResponse>;
export const TimelineResponse = SavedTimeline.merge( export const TimelineResponse = SavedTimeline.merge(SavedTimelineWithSavedObjectId).merge(
z.object({ z.object({
eventIdToNoteIds: z.array(Note).optional(), eventIdToNoteIds: z.array(Note).nullable().optional(),
notes: z.array(Note).optional(), notes: z.array(Note).nullable().optional(),
noteIds: z.array(z.string()).optional(), noteIds: z.array(z.string()).nullable().optional(),
pinnedEventIds: z.array(z.string()).optional(), pinnedEventIds: z.array(z.string()).nullable().optional(),
pinnedEventsSaveObject: z.array(PinnedEvent).optional(), pinnedEventsSaveObject: z.array(PinnedEvent).nullable().optional(),
savedObjectId: z.string(),
version: z.string(),
}) })
); );
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 type FavoriteTimelineResponse = z.infer<typeof FavoriteTimelineResponse>;
export const FavoriteTimelineResponse = z.object({ export const FavoriteTimelineResponse = z.object({
savedObjectId: z.string(), savedObjectId: z.string(),
@ -275,6 +317,15 @@ export const FavoriteTimelineResponse = z.object({
favorite: z.array(FavoriteTimelineResult).optional(), 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 type BareNoteWithoutExternalRefs = z.infer<typeof BareNoteWithoutExternalRefs>;
export const BareNoteWithoutExternalRefs = z.object({ export const BareNoteWithoutExternalRefs = z.object({
eventId: z.string().nullable().optional(), 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 type SortFieldTimelineEnum = typeof SortFieldTimeline.enum;
export const SortFieldTimelineEnum = 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`. * 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 type ImportTimelines = z.infer<typeof ImportTimelines>;
export const ImportTimelines = SavedTimeline.merge( export const ImportTimelines = SavedTimeline.merge(
z.object({ z.object({
savedObjectId: z.string().nullable().optional(), savedObjectId: z.string().nullable(),
version: z.string().nullable().optional(), version: z.string().nullable(),
globalNotes: z.array(BareNote).nullable().optional(), pinnedEventIds: z.array(z.string()).nullable(),
eventNotes: z.array(BareNote).nullable().optional(), eventNotes: z.array(BareNote).nullable(),
pinnedEventIds: z.array(z.string()).nullable().optional(), globalNotes: z.array(BareNote).nullable(),
}) })
); );
@ -346,24 +402,14 @@ export const ImportTimelineResult = z.object({
.optional(), .optional(),
}); });
export type ExportedTimelines = z.infer<typeof ExportedTimelines>; export type TimelineErrorResponse = z.infer<typeof TimelineErrorResponse>;
export const ExportedTimelines = SavedTimeline.merge( export const TimelineErrorResponse = z.union([
z.object({ z.object({
globalNotes: z.array(Note).optional(), message: z.string(),
eventNotes: z.array(Note).optional(), status_code: z.number(),
pinnedEventIds: z.array(z.string()).optional(), }),
}) z.object({
); message: z.string(),
statusCode: z.number(),
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(),
});

View file

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

View file

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

View file

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

View file

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

View file

@ -5,13 +5,6 @@ info:
externalDocs: externalDocs:
url: https://www.elastic.co/guide/en/security/current/_pin_an_event_to_an_existing_timeline.html url: https://www.elastic.co/guide/en/security/current/_pin_an_event_to_an_existing_timeline.html
description: Documentation description: Documentation
servers:
- url: 'http://{kibana_host}:{port}'
variables:
kibana_host:
default: localhost
port:
default: '5601'
paths: paths:
/api/pinned_event: /api/pinned_event:
patch: 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 { z } from '@kbn/zod';
import { TimelineResponse } from '../model/components.gen'; import { ResolvedTimeline } from '../model/components.gen';
export type ResolveTimelineRequestQuery = z.infer<typeof ResolveTimelineRequestQuery>; export type ResolveTimelineRequestQuery = z.infer<typeof ResolveTimelineRequestQuery>;
export const ResolveTimelineRequestQuery = z.object({ export const ResolveTimelineRequestQuery = z.object({
@ -32,8 +32,9 @@ export const ResolveTimelineRequestQuery = z.object({
export type ResolveTimelineRequestQueryInput = z.input<typeof ResolveTimelineRequestQuery>; export type ResolveTimelineRequestQueryInput = z.input<typeof ResolveTimelineRequestQuery>;
export type ResolveTimelineResponse = z.infer<typeof ResolveTimelineResponse>; export type ResolveTimelineResponse = z.infer<typeof ResolveTimelineResponse>;
export const ResolveTimelineResponse = z.object({ export const ResolveTimelineResponse = z.union([
data: z.object({ z.object({
getOneTimeline: TimelineResponse.nullable(), data: ResolvedTimeline,
}), }),
}); z.object({}).strict(),
]);

View file

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

View file

@ -17,7 +17,10 @@ export {
} from './persist_note/persist_note_route.gen'; } from './persist_note/persist_note_route.gen';
export { DeleteNoteRequestBody, DeleteNoteResponse } from './delete_note/delete_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 { export {
ExportTimelinesRequestQuery, ExportTimelinesRequestQuery,
@ -40,3 +43,48 @@ export {
GetNotesResponse, GetNotesResponse,
GetNotesResult, GetNotesResult,
} from './get_notes/get_notes_route.gen'; } 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. * 2.0.
*/ */
import type { SortField, Maybe } from '../common'; import type { SortField } from '../common';
import type {
DataProviderType,
TimelineType,
TimelineStatus,
RowRendererId,
} from '../../api/timeline';
export * from './events'; export * from './events';
@ -20,122 +14,6 @@ export interface TimelineRequestSortField<Field = string> extends SortField<Fiel
esTypes: string[]; 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 { export enum FlowDirection {
uniDirectional = 'uniDirectional', uniDirectional = 'uniDirectional',
biDirectional = 'biDirectional', 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

@ -268,18 +268,20 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: '#/components/schemas/TimelineResponse' type: object
nullable: true properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- getOneTimeline
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: Indicates that the (template) Timeline was found and returned. description: Indicates that the (template) Timeline was found and returned.
summary: Get Timeline or Timeline template details summary: Get Timeline or Timeline template details
tags: tags:
@ -316,22 +318,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -393,20 +380,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- persistTimeline
required:
- data
description: Indicates the Timeline was successfully created. description: Indicates the Timeline was successfully created.
'405': '405':
content: content:
@ -423,6 +397,36 @@ paths:
tags: tags:
- Security Timeline API - Security Timeline API
- access:securitySolution - 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: /api/timeline/_draft:
get: get:
description: >- description: >-
@ -441,22 +445,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: Indicates that the draft Timeline was successfully retrieved. description: Indicates that the draft Timeline was successfully retrieved.
'403': '403':
content: content:
@ -518,22 +507,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -688,28 +662,14 @@ paths:
schema: schema:
type: object type: object
properties: properties:
file: file: {}
allOf: isImmutable:
- $ref: '#/components/schemas/Readable' enum:
- type: object - 'true'
properties: - 'false'
hapi: type: string
type: object required:
properties: - file
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
description: The Timelines to import as a readable stream. description: The Timelines to import as a readable stream.
required: true required: true
responses: responses:
@ -717,12 +677,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/ImportTimelineResult'
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
description: Indicates the import of Timelines was successful. description: Indicates the import of Timelines was successful.
'400': '400':
content: content:
@ -781,7 +736,8 @@ paths:
properties: properties:
prepackagedTimelines: prepackagedTimelines:
items: items:
$ref: '#/components/schemas/SavedTimeline' $ref: '#/components/schemas/TimelineSavedToReturnObject'
nullable: true
type: array type: array
timelinesToInstall: timelinesToInstall:
items: items:
@ -804,12 +760,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/ImportTimelineResult'
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
description: Indicates the installation of prepackaged Timelines was successful. description: Indicates the installation of prepackaged Timelines was successful.
'500': '500':
content: content:
@ -847,18 +798,15 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: '#/components/schemas/TimelineResponse' $ref: '#/components/schemas/ResolvedTimeline'
nullable: true
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: The (template) Timeline has been found description: The (template) Timeline has been found
'400': '400':
description: The request is missing parameters description: The request is missing parameters
@ -927,35 +875,25 @@ paths:
schema: schema:
type: object type: object
properties: properties:
data: customTemplateTimelineCount:
type: object type: number
properties: defaultTimelineCount:
customTemplateTimelineCount: type: number
type: number elasticTemplateTimelineCount:
defaultTimelineCount: type: number
type: number favoriteCount:
elasticTemplateTimelineCount: type: number
type: number templateTimelineCount:
favoriteCount: type: number
type: number timeline:
templateTimelineCount: items:
type: number $ref: '#/components/schemas/TimelineResponse'
timelines: type: array
items: totalCount:
$ref: '#/components/schemas/TimelineResponse' type: number
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
required: required:
- data - timeline
- totalCount
description: Indicates that the (template) Timelines were found and returned. description: Indicates that the (template) Timelines were found and returned.
'400': '400':
content: content:
@ -1025,30 +963,39 @@ components:
type: object type: object
properties: properties:
aggregatable: aggregatable:
nullable: true
type: boolean type: boolean
category: category:
nullable: true
type: string type: string
columnHeaderType: columnHeaderType:
nullable: true
type: string type: string
description: description:
nullable: true
type: string type: string
example: example:
oneOf: nullable: true
- type: string type: string
- type: number
id: id:
nullable: true
type: string type: string
indexes: indexes:
items: items:
type: string type: string
nullable: true
type: array type: array
name: name:
nullable: true
type: string type: string
placeholder: placeholder:
nullable: true
type: string type: string
searchable: searchable:
nullable: true
type: boolean type: boolean
type: type:
nullable: true
type: string type: string
DataProviderQueryMatch: DataProviderQueryMatch:
type: object type: object
@ -1070,6 +1017,10 @@ components:
type: string type: string
queryMatch: queryMatch:
$ref: '#/components/schemas/QueryMatchResult' $ref: '#/components/schemas/QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/DataProviderType'
nullable: true
DataProviderResult: DataProviderResult:
type: object type: object
properties: properties:
@ -1157,41 +1108,59 @@ components:
type: object type: object
properties: properties:
exists: exists:
type: boolean nullable: true
type: string
match_all: match_all:
nullable: true
type: string type: string
meta: meta:
nullable: true
type: object type: object
properties: properties:
alias: alias:
nullable: true
type: string type: string
controlledBy: controlledBy:
nullable: true
type: string type: string
disabled: disabled:
nullable: true
type: boolean type: boolean
field: field:
nullable: true
type: string type: string
formattedValue: formattedValue:
nullable: true
type: string type: string
index: index:
nullable: true
type: string type: string
key: key:
nullable: true
type: string type: string
negate: negate:
nullable: true
type: boolean type: boolean
params: params:
nullable: true
type: string type: string
type: type:
nullable: true
type: string type: string
value: value:
nullable: true
type: string type: string
missing: missing:
nullable: true
type: string type: string
query: query:
nullable: true
type: string type: string
range: range:
nullable: true
type: string type: string
script: script:
nullable: true
type: string type: string
GetNotesResult: GetNotesResult:
type: object type: object
@ -1256,6 +1225,12 @@ components:
version: version:
nullable: true nullable: true
type: string type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Note: Note:
allOf: allOf:
- $ref: '#/components/schemas/BareNote' - $ref: '#/components/schemas/BareNote'
@ -1275,6 +1250,23 @@ components:
- $ref: '#/components/schemas/PinnedEventBaseResponseBody' - $ref: '#/components/schemas/PinnedEventBaseResponseBody'
- nullable: true - nullable: true
type: object 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: PinnedEvent:
allOf: allOf:
- $ref: '#/components/schemas/BarePinnedEvent' - $ref: '#/components/schemas/BarePinnedEvent'
@ -1312,34 +1304,27 @@ components:
nullable: true nullable: true
type: string type: string
value: value:
nullable: true oneOf:
type: string - nullable: true
Readable: type: string
- items:
type: string
nullable: true
type: array
ResolvedTimeline:
type: object type: object
properties: properties:
_data: alias_purpose:
additionalProperties: true $ref: '#/components/schemas/SavedObjectResolveAliasPurpose'
type: object alias_target_id:
_encoding:
type: string type: string
_events: outcome:
additionalProperties: true $ref: '#/components/schemas/SavedObjectResolveOutcome'
type: object timeline:
_eventsCount: $ref: '#/components/schemas/TimelineSavedToReturnObject'
type: number required:
_maxListeners: - timeline
additionalProperties: true - outcome
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
ResponseNote: ResponseNote:
type: object type: object
properties: properties:
@ -1374,6 +1359,17 @@ components:
- threat_match - threat_match
- zeek - zeek
type: string type: string
SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
SavedTimeline: SavedTimeline:
type: object type: object
properties: properties:
@ -1402,12 +1398,16 @@ components:
properties: properties:
end: end:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
start: start:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
description: description:
nullable: true nullable: true
type: string type: string
@ -1496,6 +1496,18 @@ components:
updatedBy: updatedBy:
nullable: true nullable: true
type: string type: string
SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
SerializedFilterQueryResult: SerializedFilterQueryResult:
type: object type: object
properties: properties:
@ -1545,27 +1557,63 @@ components:
TimelineResponse: TimelineResponse:
allOf: allOf:
- $ref: '#/components/schemas/SavedTimeline' - $ref: '#/components/schemas/SavedTimeline'
- $ref: '#/components/schemas/SavedTimelineWithSavedObjectId'
- type: object - type: object
properties: properties:
eventIdToNoteIds: eventIdToNoteIds:
items: items:
$ref: '#/components/schemas/Note' $ref: '#/components/schemas/Note'
nullable: true
type: array type: array
noteIds: noteIds:
items: items:
type: string type: string
nullable: true
type: array type: array
notes: notes:
items: items:
$ref: '#/components/schemas/Note' $ref: '#/components/schemas/Note'
nullable: true
type: array type: array
pinnedEventIds: pinnedEventIds:
items: items:
type: string type: string
nullable: true
type: array type: array
pinnedEventsSaveObject: pinnedEventsSaveObject:
items: items:
$ref: '#/components/schemas/PinnedEvent' $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 type: array
savedObjectId: savedObjectId:
type: string type: string

View file

@ -268,18 +268,20 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: '#/components/schemas/TimelineResponse' type: object
nullable: true properties:
getOneTimeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- getOneTimeline
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: Indicates that the (template) Timeline was found and returned. description: Indicates that the (template) Timeline was found and returned.
summary: Get Timeline or Timeline template details summary: Get Timeline or Timeline template details
tags: tags:
@ -316,22 +318,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -393,20 +380,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- persistTimeline
required:
- data
description: Indicates the Timeline was successfully created. description: Indicates the Timeline was successfully created.
'405': '405':
content: content:
@ -423,6 +397,36 @@ paths:
tags: tags:
- Security Timeline API - Security Timeline API
- access:securitySolution - 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: /api/timeline/_draft:
get: get:
description: >- description: >-
@ -441,22 +445,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: Indicates that the draft Timeline was successfully retrieved. description: Indicates that the draft Timeline was successfully retrieved.
'403': '403':
content: content:
@ -518,22 +507,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/PersistTimelineResponse'
properties:
data:
type: object
properties:
persistTimeline:
type: object
properties:
timeline:
$ref: '#/components/schemas/TimelineResponse'
required:
- timeline
required:
- persistTimeline
required:
- data
description: >- description: >-
Indicates that the draft Timeline was successfully created. In the Indicates that the draft Timeline was successfully created. In the
event the user already has a draft Timeline, the existing draft event the user already has a draft Timeline, the existing draft
@ -688,28 +662,14 @@ paths:
schema: schema:
type: object type: object
properties: properties:
file: file: {}
allOf: isImmutable:
- $ref: '#/components/schemas/Readable' enum:
- type: object - 'true'
properties: - 'false'
hapi: type: string
type: object required:
properties: - file
filename:
type: string
headers:
type: object
isImmutable:
enum:
- 'true'
- 'false'
type: string
required:
- filename
- headers
required:
- hapi
description: The Timelines to import as a readable stream. description: The Timelines to import as a readable stream.
required: true required: true
responses: responses:
@ -717,12 +677,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/ImportTimelineResult'
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
description: Indicates the import of Timelines was successful. description: Indicates the import of Timelines was successful.
'400': '400':
content: content:
@ -781,7 +736,8 @@ paths:
properties: properties:
prepackagedTimelines: prepackagedTimelines:
items: items:
$ref: '#/components/schemas/SavedTimeline' $ref: '#/components/schemas/TimelineSavedToReturnObject'
nullable: true
type: array type: array
timelinesToInstall: timelinesToInstall:
items: items:
@ -804,12 +760,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object $ref: '#/components/schemas/ImportTimelineResult'
properties:
data:
$ref: '#/components/schemas/ImportTimelineResult'
required:
- data
description: Indicates the installation of prepackaged Timelines was successful. description: Indicates the installation of prepackaged Timelines was successful.
'500': '500':
content: content:
@ -847,18 +798,15 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: object oneOf:
properties: - type: object
data:
type: object
properties: properties:
getOneTimeline: data:
$ref: '#/components/schemas/TimelineResponse' $ref: '#/components/schemas/ResolvedTimeline'
nullable: true
required: required:
- getOneTimeline - data
required: - additionalProperties: false
- data type: object
description: The (template) Timeline has been found description: The (template) Timeline has been found
'400': '400':
description: The request is missing parameters description: The request is missing parameters
@ -927,35 +875,25 @@ paths:
schema: schema:
type: object type: object
properties: properties:
data: customTemplateTimelineCount:
type: object type: number
properties: defaultTimelineCount:
customTemplateTimelineCount: type: number
type: number elasticTemplateTimelineCount:
defaultTimelineCount: type: number
type: number favoriteCount:
elasticTemplateTimelineCount: type: number
type: number templateTimelineCount:
favoriteCount: type: number
type: number timeline:
templateTimelineCount: items:
type: number $ref: '#/components/schemas/TimelineResponse'
timelines: type: array
items: totalCount:
$ref: '#/components/schemas/TimelineResponse' type: number
type: array
totalCount:
type: number
required:
- timelines
- totalCount
- defaultTimelineCount
- templateTimelineCount
- favoriteCount
- elasticTemplateTimelineCount
- customTemplateTimelineCount
required: required:
- data - timeline
- totalCount
description: Indicates that the (template) Timelines were found and returned. description: Indicates that the (template) Timelines were found and returned.
'400': '400':
content: content:
@ -1025,30 +963,39 @@ components:
type: object type: object
properties: properties:
aggregatable: aggregatable:
nullable: true
type: boolean type: boolean
category: category:
nullable: true
type: string type: string
columnHeaderType: columnHeaderType:
nullable: true
type: string type: string
description: description:
nullable: true
type: string type: string
example: example:
oneOf: nullable: true
- type: string type: string
- type: number
id: id:
nullable: true
type: string type: string
indexes: indexes:
items: items:
type: string type: string
nullable: true
type: array type: array
name: name:
nullable: true
type: string type: string
placeholder: placeholder:
nullable: true
type: string type: string
searchable: searchable:
nullable: true
type: boolean type: boolean
type: type:
nullable: true
type: string type: string
DataProviderQueryMatch: DataProviderQueryMatch:
type: object type: object
@ -1070,6 +1017,10 @@ components:
type: string type: string
queryMatch: queryMatch:
$ref: '#/components/schemas/QueryMatchResult' $ref: '#/components/schemas/QueryMatchResult'
nullable: true
type:
$ref: '#/components/schemas/DataProviderType'
nullable: true
DataProviderResult: DataProviderResult:
type: object type: object
properties: properties:
@ -1157,41 +1108,59 @@ components:
type: object type: object
properties: properties:
exists: exists:
type: boolean nullable: true
type: string
match_all: match_all:
nullable: true
type: string type: string
meta: meta:
nullable: true
type: object type: object
properties: properties:
alias: alias:
nullable: true
type: string type: string
controlledBy: controlledBy:
nullable: true
type: string type: string
disabled: disabled:
nullable: true
type: boolean type: boolean
field: field:
nullable: true
type: string type: string
formattedValue: formattedValue:
nullable: true
type: string type: string
index: index:
nullable: true
type: string type: string
key: key:
nullable: true
type: string type: string
negate: negate:
nullable: true
type: boolean type: boolean
params: params:
nullable: true
type: string type: string
type: type:
nullable: true
type: string type: string
value: value:
nullable: true
type: string type: string
missing: missing:
nullable: true
type: string type: string
query: query:
nullable: true
type: string type: string
range: range:
nullable: true
type: string type: string
script: script:
nullable: true
type: string type: string
GetNotesResult: GetNotesResult:
type: object type: object
@ -1256,6 +1225,12 @@ components:
version: version:
nullable: true nullable: true
type: string type: string
required:
- savedObjectId
- version
- pinnedEventIds
- eventNotes
- globalNotes
Note: Note:
allOf: allOf:
- $ref: '#/components/schemas/BareNote' - $ref: '#/components/schemas/BareNote'
@ -1275,6 +1250,23 @@ components:
- $ref: '#/components/schemas/PinnedEventBaseResponseBody' - $ref: '#/components/schemas/PinnedEventBaseResponseBody'
- nullable: true - nullable: true
type: object 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: PinnedEvent:
allOf: allOf:
- $ref: '#/components/schemas/BarePinnedEvent' - $ref: '#/components/schemas/BarePinnedEvent'
@ -1312,34 +1304,27 @@ components:
nullable: true nullable: true
type: string type: string
value: value:
nullable: true oneOf:
type: string - nullable: true
Readable: type: string
- items:
type: string
nullable: true
type: array
ResolvedTimeline:
type: object type: object
properties: properties:
_data: alias_purpose:
additionalProperties: true $ref: '#/components/schemas/SavedObjectResolveAliasPurpose'
type: object alias_target_id:
_encoding:
type: string type: string
_events: outcome:
additionalProperties: true $ref: '#/components/schemas/SavedObjectResolveOutcome'
type: object timeline:
_eventsCount: $ref: '#/components/schemas/TimelineSavedToReturnObject'
type: number required:
_maxListeners: - timeline
additionalProperties: true - outcome
type: object
_position:
type: number
_read:
additionalProperties: true
type: object
_readableState:
additionalProperties: true
type: object
readable:
type: boolean
ResponseNote: ResponseNote:
type: object type: object
properties: properties:
@ -1374,6 +1359,17 @@ components:
- threat_match - threat_match
- zeek - zeek
type: string type: string
SavedObjectResolveAliasPurpose:
enum:
- savedObjectConversion
- savedObjectImport
type: string
SavedObjectResolveOutcome:
enum:
- exactMatch
- aliasMatch
- conflict
type: string
SavedTimeline: SavedTimeline:
type: object type: object
properties: properties:
@ -1402,12 +1398,16 @@ components:
properties: properties:
end: end:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
start: start:
oneOf: oneOf:
- type: string - nullable: true
- type: number type: string
- nullable: true
type: number
description: description:
nullable: true nullable: true
type: string type: string
@ -1496,6 +1496,18 @@ components:
updatedBy: updatedBy:
nullable: true nullable: true
type: string type: string
SavedTimelineWithSavedObjectId:
allOf:
- $ref: '#/components/schemas/SavedTimeline'
- type: object
properties:
savedObjectId:
type: string
version:
type: string
required:
- savedObjectId
- version
SerializedFilterQueryResult: SerializedFilterQueryResult:
type: object type: object
properties: properties:
@ -1545,27 +1557,63 @@ components:
TimelineResponse: TimelineResponse:
allOf: allOf:
- $ref: '#/components/schemas/SavedTimeline' - $ref: '#/components/schemas/SavedTimeline'
- $ref: '#/components/schemas/SavedTimelineWithSavedObjectId'
- type: object - type: object
properties: properties:
eventIdToNoteIds: eventIdToNoteIds:
items: items:
$ref: '#/components/schemas/Note' $ref: '#/components/schemas/Note'
nullable: true
type: array type: array
noteIds: noteIds:
items: items:
type: string type: string
nullable: true
type: array type: array
notes: notes:
items: items:
$ref: '#/components/schemas/Note' $ref: '#/components/schemas/Note'
nullable: true
type: array type: array
pinnedEventIds: pinnedEventIds:
items: items:
type: string type: string
nullable: true
type: array type: array
pinnedEventsSaveObject: pinnedEventsSaveObject:
items: items:
$ref: '#/components/schemas/PinnedEvent' $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 type: array
savedObjectId: savedObjectId:
type: string type: string

View file

@ -9,9 +9,10 @@ import { FilterStateStore } from '@kbn/es-query';
import type { DataTableModel } from '@kbn/securitysolution-data-table'; import type { DataTableModel } from '@kbn/securitysolution-data-table';
import { VIEW_SELECTION } from '../../../common/constants'; import { VIEW_SELECTION } from '../../../common/constants';
import type { TimelineResult } from '../../../common/api/timeline';
import { TimelineId, TimelineTabs } from '../../../common/types/timeline'; import { TimelineId, TimelineTabs } from '../../../common/types/timeline';
import type { TimelineResponse } from '../../../common/api/timeline';
import { import {
type ColumnHeaderResult,
RowRendererIdEnum, RowRendererIdEnum,
TimelineTypeEnum, TimelineTypeEnum,
TimelineStatusEnum, TimelineStatusEnum,
@ -1986,9 +1987,11 @@ export const mockDataTableModel: DataTableModel = {
}, },
}; };
export const mockGetOneTimelineResult: TimelineResult = { export const mockGetOneTimelineResult: TimelineResponse = {
savedObjectId: 'ef579e40-jibber-jabber', 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' }, dateRange: { start: '2020-03-18T13:46:38.929Z', end: '2020-03-18T13:52:38.929Z' },
description: 'This is a sample rule description', description: 'This is a sample rule description',
eventType: 'all', eventType: 'all',

View file

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

View file

@ -20,12 +20,16 @@ import {
isUntitled, isUntitled,
omitTypenameInTimeline, omitTypenameInTimeline,
useQueryTimelineById, useQueryTimelineById,
formatTimelineResultToModel, formatTimelineResponseToModel,
} from './helpers'; } from './helpers';
import type { OpenTimelineResult } from './types'; import type { OpenTimelineResult } from './types';
import { TimelineId } from '../../../../common/types/timeline'; import { TimelineId } from '../../../../common/types/timeline';
import type { RowRendererId } from '../../../../common/api/timeline'; import {
import { TimelineTypeEnum, TimelineStatusEnum } from '../../../../common/api/timeline'; TimelineTypeEnum,
TimelineStatusEnum,
type ColumnHeaderResult,
type RowRendererId,
} from '../../../../common/api/timeline';
import { import {
mockTimeline as mockSelectedTimeline, mockTimeline as mockSelectedTimeline,
mockTemplate as mockSelectedTemplate, mockTemplate as mockSelectedTemplate,
@ -379,7 +383,7 @@ describe('helpers', () => {
); );
const timeline = { const timeline = {
savedObjectId: 'savedObject-1', savedObjectId: 'savedObject-1',
columns: columnsWithoutEventAction, columns: columnsWithoutEventAction as ColumnHeaderResult[],
version: '1', version: '1',
}; };
@ -396,7 +400,7 @@ describe('helpers', () => {
); );
const timeline = { const timeline = {
savedObjectId: 'savedObject-1', savedObjectId: 'savedObject-1',
columns: columnsWithoutEventAction, columns: columnsWithoutEventAction as ColumnHeaderResult[],
filters: [ filters: [
{ {
meta: { meta: {
@ -568,7 +572,7 @@ describe('helpers', () => {
version: '1', version: '1',
status: TimelineStatusEnum.active, status: TimelineStatusEnum.active,
timelineType: TimelineTypeEnum.default, timelineType: TimelineTypeEnum.default,
columns: customColumns, columns: customColumns as ColumnHeaderResult[],
}; };
const newTimeline = defaultTimelineToTimelineModel( const newTimeline = defaultTimelineToTimelineModel(
@ -691,7 +695,7 @@ describe('helpers', () => {
}); });
test('Do not override daterange if TimelineStatus is active', () => { test('Do not override daterange if TimelineStatus is active', () => {
const { timeline } = formatTimelineResultToModel( const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
args.duplicate, args.duplicate,
args.timelineType args.timelineType
@ -744,7 +748,7 @@ describe('helpers', () => {
}); });
test('should not override daterange if TimelineStatus is active', () => { test('should not override daterange if TimelineStatus is active', () => {
const { timeline } = formatTimelineResultToModel( const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
args.duplicate, args.duplicate,
args.timelineType args.timelineType
@ -818,7 +822,7 @@ describe('helpers', () => {
}); });
test('override daterange if TimelineStatus is immutable', () => { test('override daterange if TimelineStatus is immutable', () => {
const { timeline } = formatTimelineResultToModel( const { timeline } = formatTimelineResponseToModel(
omitTypenameInTimeline(getOr({}, 'data.timeline', template)), omitTypenameInTimeline(getOr({}, 'data.timeline', template)),
args.duplicate, args.duplicate,
args.timelineType 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 { useDiscoverInTimelineContext } from '../../../common/components/discover_in_timeline/use_discover_in_timeline_context';
import type { ColumnHeaderOptions } from '../../../../common/types/timeline'; import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
import type { import type {
TimelineResult, TimelineResponse,
SingleTimelineResolveResponse, ResolvedTimeline,
ColumnHeaderResult, ColumnHeaderResult,
FilterTimelineResult, FilterTimelineResult,
DataProviderResult, DataProviderResult,
@ -80,7 +80,7 @@ export const isUntitled = ({ title }: OpenTimelineResult): boolean =>
const omitTypename = (key: string, value: keyof TimelineModel) => const omitTypename = (key: string, value: keyof TimelineModel) =>
key === '__typename' ? undefined : value; key === '__typename' ? undefined : value;
export const omitTypenameInTimeline = (timeline: TimelineResult): TimelineResult => export const omitTypenameInTimeline = (timeline: TimelineResponse): TimelineResponse =>
JSON.parse(JSON.stringify(timeline), omitTypename); JSON.parse(JSON.stringify(timeline), omitTypename);
const parseString = (params: string) => { const parseString = (params: string) => {
@ -164,7 +164,7 @@ const setPinnedEventIds = (duplicate: boolean, pinnedEventIds: string[] | null |
: {}; : {};
const getTemplateTimelineId = ( const getTemplateTimelineId = (
timeline: TimelineResult, timeline: TimelineResponse,
duplicate: boolean, duplicate: boolean,
targetTimelineType?: TimelineType targetTimelineType?: TimelineType
) => { ) => {
@ -200,7 +200,7 @@ const convertToDefaultField = ({ and, ...dataProvider }: DataProviderResult) =>
const getDataProviders = ( const getDataProviders = (
duplicate: boolean, duplicate: boolean,
dataProviders: TimelineResult['dataProviders'], dataProviders: TimelineResponse['dataProviders'],
timelineType?: TimelineType timelineType?: TimelineType
) => { ) => {
if (duplicate && dataProviders && timelineType === TimelineTypeEnum.default) { if (duplicate && dataProviders && timelineType === TimelineTypeEnum.default) {
@ -214,7 +214,7 @@ const getDataProviders = (
}; };
export const getTimelineTitle = ( export const getTimelineTitle = (
timeline: TimelineResult, timeline: TimelineResponse,
duplicate: boolean, duplicate: boolean,
timelineType?: TimelineType timelineType?: TimelineType
) => { ) => {
@ -225,7 +225,7 @@ export const getTimelineTitle = (
}; };
export const getTimelineStatus = ( export const getTimelineStatus = (
timeline: TimelineResult, timeline: TimelineResponse,
duplicate: boolean, duplicate: boolean,
timelineType?: TimelineType timelineType?: TimelineType
) => { ) => {
@ -236,7 +236,7 @@ export const getTimelineStatus = (
}; };
export const defaultTimelineToTimelineModel = ( export const defaultTimelineToTimelineModel = (
timeline: TimelineResult, timeline: TimelineResponse,
duplicate: boolean, duplicate: boolean,
timelineType?: TimelineType, timelineType?: TimelineType,
unifiedComponentsInTimelineDisabled?: boolean unifiedComponentsInTimelineDisabled?: boolean
@ -291,8 +291,8 @@ export const defaultTimelineToTimelineModel = (
); );
}; };
export const formatTimelineResultToModel = ( export const formatTimelineResponseToModel = (
timelineToOpen: TimelineResult, timelineToOpen: TimelineResponse,
duplicate: boolean = false, duplicate: boolean = false,
timelineType?: TimelineType, timelineType?: TimelineType,
unifiedComponentsInTimelineDisabled?: boolean unifiedComponentsInTimelineDisabled?: boolean
@ -376,12 +376,12 @@ export const useQueryTimelineById = () => {
} else { } else {
return Promise.resolve(resolveTimeline(timelineId)) return Promise.resolve(resolveTimeline(timelineId))
.then((result) => { .then((result) => {
const data: SingleTimelineResolveResponse['data'] | null = getOr(null, 'data', result); const data: ResolvedTimeline | null = getOr(null, 'data', result);
if (!data) return; if (!data) return;
const timelineToOpen = omitTypenameInTimeline(data.timeline); const timelineToOpen = omitTypenameInTimeline(data.timeline);
const { timeline, notes } = formatTimelineResultToModel( const { timeline, notes } = formatTimelineResponseToModel(
timelineToOpen, timelineToOpen,
duplicate, duplicate,
timelineType, timelineType,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -74,7 +74,7 @@ export const addPinnedEventToTimelineMiddleware: (kibana: CoreStart) => Middlewa
const currentTimeline = selectTimelineById(store.getState(), action.payload.id); const currentTimeline = selectTimelineById(store.getState(), action.payload.id);
// The response is null or empty in case we unpinned an event. // The response is null or empty in case we unpinned an event.
// In that case we want to remove the locally pinned 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( return store.dispatch(
updateTimeline({ updateTimeline({
id: action.payload.id, 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 () => { 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 })); await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled(); expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
@ -175,7 +175,7 @@ describe('Timeline save middleware', () => {
}); });
describe('#convertTimelineAsInput ', () => { describe('#convertTimelineAsInput ', () => {
test('should return a TimelineInput instead of TimelineModel ', () => { test('should return a SavedTimeline instead of TimelineModel ', () => {
const columns: TimelineModel['columns'] = [ const columns: TimelineModel['columns'] = [
{ {
columnHeaderType: 'not-filtered', columnHeaderType: 'not-filtered',

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,11 +5,7 @@
* 2.0. * 2.0.
*/ */
import type * as rt from 'io-ts';
import type { Transform } from 'stream'; 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 { createConcatStream, createSplitStream, createMapStream } from '@kbn/utils';
import { BadRequestError } from '@kbn/securitysolution-es-utils'; import { BadRequestError } from '@kbn/securitysolution-es-utils';
import { import {
@ -19,23 +15,15 @@ import {
} from '../../../../../utils/read_stream/create_stream_from_ndjson'; } from '../../../../../utils/read_stream/create_stream_from_ndjson';
import type { ImportTimelineResponse } from './types'; import type { ImportTimelineResponse } from './types';
import { ImportTimelinesSchemaRt } from '../../../../../../common/api/timeline'; import { ImportTimelines } from '../../../../../../common/api/timeline';
import { throwErrors } from '../../../utils/common'; 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); const validateTimelines = (): Transform =>
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 =>
createMapStream((obj: ImportTimelineResponse) => createMapStream((obj: ImportTimelineResponse) =>
obj instanceof Error obj instanceof Error ? new BadRequestError(obj.message) : parseOrThrow(ImportTimelines)(obj)
? new BadRequestError(obj.message)
: decodeOrThrow(ImportTimelinesSchemaRt)(obj)
); );
export const createTimelinesStreamFromNdJson = (ruleLimit: number) => { export const createTimelinesStreamFromNdJson = (ruleLimit: number) => {
return [ return [

View file

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

View file

@ -39,7 +39,6 @@ import {
describe('import timelines', () => { describe('import timelines', () => {
let server: ReturnType<typeof serverMock.create>; let server: ReturnType<typeof serverMock.create>;
let request: ReturnType<typeof requestMock.create>;
let securitySetup: SecurityPluginSetup; let securitySetup: SecurityPluginSetup;
let { context } = requestContextMock.createTools(); let { context } = requestContextMock.createTools();
let mockGetTimeline: jest.Mock; 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', () => { describe('import timeline templates', () => {
@ -904,7 +861,7 @@ describe('import timeline templates', () => {
request = requestMock.create({ request = requestMock.create({
method: 'post', method: 'post',
path: TIMELINE_EXPORT_URL, path: TIMELINE_EXPORT_URL,
body: { id: 'someId' }, body: { isImmutable: 1 },
}); });
const importTimelinesRoute = jest.requireActual('.').importTimelinesRoute; const importTimelinesRoute = jest.requireActual('.').importTimelinesRoute;
@ -912,7 +869,7 @@ describe('import timeline templates', () => {
const result = server.validate(request); const result = server.validate(request);
expect(result.badRequest).toHaveBeenCalledWith( 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 { extname } from 'path';
import type { Readable } from 'stream'; 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 { transformError } from '@kbn/securitysolution-es-utils';
import type { SecuritySolutionPluginRouter } from '../../../../../types'; import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_IMPORT_URL } from '../../../../../../common/constants'; import { TIMELINE_IMPORT_URL } from '../../../../../../common/constants';
import type { ConfigType } from '../../../../../config'; import type { ConfigType } from '../../../../../config';
import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { importTimelines } from './helpers'; import { importTimelines } from './helpers';
import { ImportTimelinesPayloadSchemaRt } from '../../../../../../common/api/timeline'; import {
ImportTimelinesRequestBody,
type ImportTimelinesResponse,
} from '../../../../../../common/api/timeline';
import { buildFrameworkRequest } from '../../../utils/common'; import { buildFrameworkRequest } from '../../../utils/common';
export { importTimelines } from './helpers'; export { importTimelines } from './helpers';
@ -39,11 +44,13 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
.addVersion( .addVersion(
{ {
validate: { validate: {
request: { body: buildRouteValidationWithExcess(ImportTimelinesPayloadSchemaRt) }, request: {
body: buildRouteValidationWithZod(ImportTimelinesRequestBody),
},
}, },
version: '2023-10-31', version: '2023-10-31',
}, },
async (context, request, response) => { async (context, request, response): Promise<IKibanaResponse<ImportTimelinesResponse>> => {
try { try {
const siemResponse = buildSiemResponse(response); const siemResponse = buildSiemResponse(response);
const savedObjectsClient = (await context.core).savedObjects.client; const savedObjectsClient = (await context.core).savedObjects.client;
@ -52,7 +59,7 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
} }
const { file, isImmutable } = request.body; const { file, isImmutable } = request.body;
const { filename } = file.hapi; const filename = extractFilename(file);
const fileExtension = extname(filename).toLowerCase(); const fileExtension = extname(filename).toLowerCase();
if (fileExtension !== '.ndjson') { if (fileExtension !== '.ndjson') {
@ -69,8 +76,11 @@ export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, confi
frameworkRequest, frameworkRequest,
isImmutable === 'true' isImmutable === 'true'
); );
if (typeof res !== 'string') return response.ok({ body: res ?? {} }); if (res instanceof Error || typeof res === 'string') {
else throw res; throw res;
} else {
return response.ok({ body: res ?? {} });
}
} catch (err) { } catch (err) {
const error = transformError(err); const error = transformError(err);
const siemResponse = buildSiemResponse(response); 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 { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core/server'; import type { IKibanaResponse } from '@kbn/core/server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SecuritySolutionPluginRouter } from '../../../../../types'; import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_URL } from '../../../../../../common/constants'; 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 { 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 { buildFrameworkRequest, TimelineStatusActions } from '../../../utils/common';
import { createTimelines } from '../create_timelines'; import { createTimelines } from '../create_timelines';
import { CompareTimelinesStatus } from '../../../utils/compare_timelines_status'; 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 router.versioned
.patch({ .patch({
path: TIMELINE_URL, path: TIMELINE_URL,
@ -34,11 +34,11 @@ export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter, _: Con
.addVersion( .addVersion(
{ {
validate: { validate: {
request: { body: buildRouteValidationWithExcess(patchTimelineSchema) }, request: { body: buildRouteValidationWithZod(PatchTimelineRequestBody) },
}, },
version: '2023-10-31', version: '2023-10-31',
}, },
async (context, request, response): Promise<IKibanaResponse<PatchTimelinesResponse>> => { async (context, request, response): Promise<IKibanaResponse<PatchTimelineResponse>> => {
const siemResponse = buildSiemResponse(response); const siemResponse = buildSiemResponse(response);
try { try {

View file

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

View file

@ -6,24 +6,24 @@
*/ */
import { transformError } from '@kbn/securitysolution-es-utils'; 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 type { SecuritySolutionPluginRouter } from '../../../../../types';
import { TIMELINE_RESOLVE_URL } from '../../../../../../common/constants'; 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 { buildSiemResponse } from '../../../../detection_engine/routes/utils';
import { buildFrameworkRequest } from '../../../utils/common'; import { buildFrameworkRequest } from '../../../utils/common';
import { getTimelineQuerySchema } from '../../../../../../common/api/timeline'; import {
import { getTimelineTemplateOrNull, resolveTimelineOrNull } from '../../../saved_object/timelines'; ResolveTimelineRequestQuery,
import type { type ResolveTimelineResponse,
SavedTimeline,
ResolvedTimelineWithOutcomeSavedObject,
} from '../../../../../../common/api/timeline'; } 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 router.versioned
.get({ .get({
path: TIMELINE_RESOLVE_URL, path: TIMELINE_RESOLVE_URL,
@ -36,16 +36,16 @@ export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter, _: Co
{ {
version: '2023-10-31', version: '2023-10-31',
validate: { validate: {
request: { query: buildRouteValidationWithExcess(getTimelineQuerySchema) }, request: { query: buildRouteValidationWithZod(ResolveTimelineRequestQuery) },
}, },
}, },
async (context, request, response) => { async (context, request, response): Promise<IKibanaResponse<ResolveTimelineResponse>> => {
try { try {
const frameworkRequest = await buildFrameworkRequest(context, request); const frameworkRequest = await buildFrameworkRequest(context, request);
const query = request.query ?? {}; const query = request.query ?? {};
const { template_timeline_id: templateTimelineId, id } = 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) { if (templateTimelineId != null && id == null) {
// Template timelineId is not a SO id, so it does not need to be updated to use resolve // 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 ( export const persistNotes = async (
frameworkRequest: FrameworkRequest, frameworkRequest: FrameworkRequest,
timelineSavedObjectId: string, timelineSavedObjectId: string,
existingNoteIds?: string[], existingNoteIds?: string[] | null,
newNotes?: Note[], newNotes?: Note[],
overrideOwner: boolean = true overrideOwner: boolean = true
) => { ) => {

View file

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

View file

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

View file

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

View file

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

View file

@ -7,9 +7,9 @@
import path, { join, resolve } from 'path'; import path, { join, resolve } from 'path';
import type { import type {
CheckTimelineStatusRt, TimelineResponse,
TimelineSavedObject, ImportTimelines,
ImportTimelinesSchema, InstallPrepackedTimelinesRequestBody,
} from '../../../../common/api/timeline'; } from '../../../../common/api/timeline';
import type { FrameworkRequest } from '../../framework'; import type { FrameworkRequest } from '../../framework';
@ -19,9 +19,9 @@ import { getExistingPrepackagedTimelines } from '../saved_object/timelines';
import { loadData, getReadables } from './common'; import { loadData, getReadables } from './common';
export const getTimelinesToUpdate = ( export const getTimelinesToUpdate = (
timelinesFromFileSystem: ImportTimelinesSchema[], timelinesFromFileSystem: ImportTimelines[],
installedTimelines: TimelineSavedObject[] installedTimelines: TimelineResponse[]
): ImportTimelinesSchema[] => { ): ImportTimelines[] => {
return timelinesFromFileSystem.filter((timeline) => return timelinesFromFileSystem.filter((timeline) =>
installedTimelines.some((installedTimeline) => { installedTimelines.some((installedTimeline) => {
return ( return (
@ -34,9 +34,9 @@ export const getTimelinesToUpdate = (
}; };
export const getTimelinesToInstall = ( export const getTimelinesToInstall = (
timelinesFromFileSystem: ImportTimelinesSchema[], timelinesFromFileSystem: ImportTimelines[],
installedTimelines: TimelineSavedObject[] installedTimelines: TimelineResponse[]
): ImportTimelinesSchema[] => { ): ImportTimelines[] => {
return timelinesFromFileSystem.filter( return timelinesFromFileSystem.filter(
(timeline) => (timeline) =>
!installedTimelines.some( !installedTimelines.some(
@ -49,11 +49,11 @@ export const checkTimelinesStatus = async (
frameworkRequest: FrameworkRequest, frameworkRequest: FrameworkRequest,
filePath?: string, filePath?: string,
fileName?: string fileName?: string
): Promise<CheckTimelineStatusRt | Error> => { ): Promise<InstallPrepackedTimelinesRequestBody | Error> => {
let readStream; let readStream;
let timeline: { let timeline: {
totalCount: number; totalCount: number;
timeline: TimelineSavedObject[]; timeline: TimelineResponse[];
}; };
const dir = resolve( const dir = resolve(
join( join(
@ -75,7 +75,7 @@ export const checkTimelinesStatus = async (
}; };
} }
return loadData<'utf-8', CheckTimelineStatusRt>( return loadData<'utf-8', InstallPrepackedTimelinesRequestBody>(
readStream, readStream,
<T>(timelinesFromFileSystem: T) => { <T>(timelinesFromFileSystem: T) => {
if (Array.isArray(timelinesFromFileSystem)) { 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; you may not use this file except in compliance with the Elastic License
* 2.0. * 2.0.
*/ */
import type * as rt from 'io-ts';
import { set } from '@kbn/safer-lodash-set/fp'; import { set } from '@kbn/safer-lodash-set/fp';
import readline from 'readline'; import readline from 'readline';
import fs from 'fs'; import fs from 'fs';
@ -13,7 +13,6 @@ import { createListStream } from '@kbn/utils';
import { schema } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema';
import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server';
import { formatErrors } from '@kbn/securitysolution-io-ts-utils';
import type { FrameworkRequest } from '../../framework'; import type { FrameworkRequest } from '../../framework';
@ -38,12 +37,6 @@ export const buildFrameworkRequest = async (
export const escapeHatch = schema.object({}, { unknowns: 'allow' }); 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> => export const getReadables = (dataPath: string): Promise<Readable> =>
new Promise((resolved, reject) => { new Promise((resolved, reject) => {
const contents: string[] = []; const contents: string[] = [];

View file

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

View file

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

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