[Alerting] adds Connectors APIs and deprecates old Actions APIs as per the new Alerting terminology (#92451)

* moved legacy actions api to legacy folder

* introduced connector create api

* added new delete route

* added new execute and get_all

* introduced all connector APIs

* renamed action to connector in Apis

* comment on camel case type

* fixed va

* updated docs

* legacy title

* corrected APIs

* legacy links

* added linik to deprecatred APIs

* added linik to deprecatred APIs from index

* moved legacy apis down one level

* Apply suggestions from code review

Co-authored-by: ymao1 <ying.mao@elastic.co>

* renamed route file for connectorTypesRoute

* define legacy route

* Update docs/api/actions-and-connectors/legacy/index.asciidoc

Co-authored-by: Mike Côté <mikecote@users.noreply.github.com>

* api docs

Co-authored-by: ymao1 <ying.mao@elastic.co>
Co-authored-by: Mike Côté <mikecote@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Gidi Meir Morris 2021-03-05 11:49:06 +00:00 committed by GitHub
parent 46247c6c55
commit 7cfd15c038
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 3373 additions and 864 deletions

View file

@ -647,7 +647,9 @@
"section": "def-server.ActionResult",
"text": "ActionResult"
},
"<Record<string, unknown>>>; delete: ({ id }: { id: string; }) => Promise<{}>; create: ({ action: { actionTypeId, name, config, secrets }, }: CreateOptions) => Promise<",
"<Record<string, unknown>>>; delete: ({ id }: { id: string; }) => Promise<{}>; create: ({ action: { actionTypeId, name, config, secrets }, }: ",
"CreateOptions",
") => Promise<",
{
"pluginId": "actions",
"scope": "server",
@ -655,23 +657,15 @@
"section": "def-server.ActionResult",
"text": "ActionResult"
},
"<Record<string, unknown>>>; update: ({ id, action }: UpdateOptions) => Promise<",
"<Record<string, unknown>>>; update: ({ id, action }: ",
"UpdateOptions",
") => Promise<",
{
"pluginId": "actions",
"scope": "server",
"docId": "kibActionsPluginApi",
"section": "def-server.ActionResult",
"text": "ActionResult"
},
"<Record<string, unknown>>>; execute: ({ actionId, params, source, }: Pick<",
"ExecuteOptions",
"<unknown>, \"source\" | \"params\" | \"actionId\">) => Promise<",
{
"pluginId": "actions",
"scope": "common",
"docId": "kibActionsPluginApi",
"section": "def-common.ActionTypeExecutorResult",
"text": "ActionTypeExecutorResult"
}
],
"initialIsOpen": false
@ -1071,8 +1065,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 94,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L94"
"lineNumber": 86,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L86"
}
}
],
@ -1080,15 +1074,15 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 88,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L88"
"lineNumber": 80,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L80"
}
}
],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 87,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L87"
"lineNumber": 79,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L79"
},
"lifecycle": "setup",
"initialIsOpen": true
@ -1119,8 +1113,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 99,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L99"
"lineNumber": 91,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L91"
}
},
{
@ -1138,15 +1132,15 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 99,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L99"
"lineNumber": 91,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L91"
}
}
],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 99,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L99"
"lineNumber": 91,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L91"
}
}
],
@ -1154,8 +1148,8 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 99,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L99"
"lineNumber": 91,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L91"
}
},
{
@ -1177,8 +1171,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 101,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L101"
"lineNumber": 93,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L93"
}
},
{
@ -1191,8 +1185,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 102,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L102"
"lineNumber": 94,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L94"
}
},
{
@ -1210,15 +1204,15 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 103,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L103"
"lineNumber": 95,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L95"
}
}
],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 103,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L103"
"lineNumber": 95,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L95"
}
}
],
@ -1226,8 +1220,8 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 100,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L100"
"lineNumber": 92,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L92"
}
},
{
@ -1272,8 +1266,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 105,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L105"
"lineNumber": 97,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L97"
}
}
],
@ -1281,8 +1275,8 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 105,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L105"
"lineNumber": 97,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L97"
}
},
{
@ -1327,8 +1321,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 106,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L106"
"lineNumber": 98,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L98"
}
}
],
@ -1336,8 +1330,8 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 106,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L106"
"lineNumber": 98,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L98"
}
},
{
@ -1348,8 +1342,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 107,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L107"
"lineNumber": 99,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L99"
},
"signature": [
{
@ -1381,8 +1375,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 109,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L109"
"lineNumber": 101,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L101"
}
},
{
@ -1395,8 +1389,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 110,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L110"
"lineNumber": 102,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L102"
}
},
{
@ -1409,8 +1403,8 @@
"description": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 111,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L111"
"lineNumber": 103,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L103"
}
}
],
@ -1418,15 +1412,15 @@
"returnComment": [],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 108,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L108"
"lineNumber": 100,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L100"
}
}
],
"source": {
"path": "x-pack/plugins/actions/server/plugin.ts",
"lineNumber": 98,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L98"
"lineNumber": 90,
"link": "https://github.com/elastic/kibana/tree/masterx-pack/plugins/actions/server/plugin.ts#L90"
},
"lifecycle": "start",
"initialIsOpen": true

View file

@ -3,21 +3,23 @@
Manage Actions and Connectors.
The following action APIs are available:
The following connector APIs are available:
* <<actions-and-connectors-api-get, Get action API>> to retrieve a single action by ID
* <<actions-and-connectors-api-get, Get connector API>> to retrieve a single connector by ID
* <<actions-and-connectors-api-get-all, Get all actions API>> to retrieve all actions
* <<actions-and-connectors-api-get-all, Get all connectors API>> to retrieve all connectors
* <<actions-and-connectors-api-list, List all action types API>> to retrieve a list of all action types
* <<actions-and-connectors-api-list, List all connector types API>> to retrieve a list of all connector types
* <<actions-and-connectors-api-create, Create action API>> to create actions
* <<actions-and-connectors-api-create, Create connector API>> to create connectors
* <<actions-and-connectors-api-update, Update action API>> to update the attributes for an existing action
* <<actions-and-connectors-api-update, Update connector API>> to update the attributes for an existing connector
* <<actions-and-connectors-api-execute, Execute action API>> to execute an action by ID
* <<actions-and-connectors-api-execute, Execute connector API>> to execute a connector by ID
* <<actions-and-connectors-api-delete, Delete action API>> to delete an action by ID
* <<actions-and-connectors-api-delete, Delete connector API>> to delete a connector by ID
For deprecated APIs, refer to <<actions-and-connectors-legacy-apis>>.
For information about the actions and connectors that {kib} supports, refer to <<action-types,Action and connector types>>.
@ -28,3 +30,11 @@ include::actions-and-connectors/create.asciidoc[]
include::actions-and-connectors/update.asciidoc[]
include::actions-and-connectors/execute.asciidoc[]
include::actions-and-connectors/delete.asciidoc[]
include::actions-and-connectors/legacy/index.asciidoc[]
include::actions-and-connectors/legacy/get.asciidoc[]
include::actions-and-connectors/legacy/get_all.asciidoc[]
include::actions-and-connectors/legacy/list.asciidoc[]
include::actions-and-connectors/legacy/create.asciidoc[]
include::actions-and-connectors/legacy/update.asciidoc[]
include::actions-and-connectors/legacy/execute.asciidoc[]
include::actions-and-connectors/legacy/delete.asciidoc[]

View file

@ -1,17 +1,17 @@
[[actions-and-connectors-api-create]]
=== Create action API
=== Create connector API
++++
<titleabbrev>Create action API</titleabbrev>
<titleabbrev>Create connector API</titleabbrev>
++++
Creates an action.
Creates a connector.
[[actions-and-connectors-api-create-request]]
==== Request
`POST <kibana host>:<port>/api/actions/action`
`POST <kibana host>:<port>/api/actions/connector`
`POST <kibana host>:<port>/s/<space_id>/api/actions/action`
`POST <kibana host>:<port>/s/<space_id>/api/actions/connector`
[[actions-and-connectors-api-create-path-params]]
==== Path parameters
@ -23,18 +23,18 @@ Creates an action.
==== Request body
`name`::
(Required, string) The display name for the action.
(Required, string) The display name for the connector.
`actionTypeId`::
(Required, string) The action type ID for the action.
`connector_type_id`::
(Required, string) The connector type ID for the connector.
`config`::
(Required, object) The configuration for the action. Configuration properties vary depending on
the action type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
(Required, object) The configuration for the connector. Configuration properties vary depending on
the connector type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
`secrets`::
(Required, object) The secrets configuration for the action. Secrets configuration properties vary
depending on the action type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
(Required, object) The secrets configuration for the connector. Secrets configuration properties vary
depending on the connector type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
+
WARNING: Remember these values. You must provide them each time you call the <<actions-and-connectors-api-update, update>> API.
@ -49,10 +49,10 @@ WARNING: Remember these values. You must provide them each time you call the <<a
[source,sh]
--------------------------------------------------
$ curl -X POST api/actions/action -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
$ curl -X POST api/actions/connector -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"name": "my-action",
"actionTypeId": ".index",
"name": "my-connector",
"connector_type_id": ".index",
"config": {
"index": "test-index"
}
@ -66,13 +66,13 @@ The API returns the following:
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-action",
"connector_type_id": ".index",
"name": "my-connector",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
"is_preconfigured": false
}
--------------------------------------------------

View file

@ -1,25 +1,25 @@
[[actions-and-connectors-api-delete]]
=== Delete action API
=== Delete connector API
++++
<titleabbrev>Delete action API</titleabbrev>
<titleabbrev>Delete connector API</titleabbrev>
++++
Deletes an action by ID.
Deletes an connector by ID.
WARNING: When you delete an action, _it cannot be recovered_.
WARNING: When you delete a connector, _it cannot be recovered_.
[[actions-and-connectors-api-delete-request]]
==== Request
`DELETE <kibana host>:<port>/api/actions/action/<id>`
`DELETE <kibana host>:<port>/api/actions/connector/<id>`
`DELETE <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
`DELETE <kibana host>:<port>/s/<space_id>/api/actions/connector/<id>`
[[actions-and-connectors-api-delete-path-params]]
==== Path parameters
`id`::
(Required, string) The ID of the action.
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
@ -34,6 +34,6 @@ WARNING: When you delete an action, _it cannot be recovered_.
[source,sh]
--------------------------------------------------
$ curl -X DELETE api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
$ curl -X DELETE api/actions/connector/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
--------------------------------------------------
// KIBANA

View file

@ -1,23 +1,23 @@
[[actions-and-connectors-api-execute]]
=== Execute action API
=== Execute connector API
++++
<titleabbrev>Execute action API</titleabbrev>
<titleabbrev>Execute connector API</titleabbrev>
++++
Executes an action by ID.
Executes a connector by ID.
[[actions-and-connectors-api-execute-request]]
==== Request
`POST <kibana host>:<port>/api/actions/action/<id>/_execute`
`POST <kibana host>:<port>/api/actions/connector/<id>/_execute`
`POST <kibana host>:<port>/s/<space_id>/api/actions/action/<id>/_execute`
`POST <kibana host>:<port>/s/<space_id>/api/actions/connector/<id>/_execute`
[[actions-and-connectors-api-execute-params]]
==== Path parameters
`id`::
(Required, string) The ID of the action.
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
@ -26,8 +26,8 @@ Executes an action by ID.
==== Request body
`params`::
(Required, object) The parameters of the action. Parameter properties vary depending on
the action type. For information about the parameter properties, refer to <<action-types,Action and connector types>>.
(Required, object) The parameters of the connector. Parameter properties vary depending on
the connector type. For information about the parameter properties, refer to <<action-types,Action and connector types>>.
[[actions-and-connectors-api-execute-codes]]
==== Response code
@ -40,7 +40,7 @@ Executes an action by ID.
[source,sh]
--------------------------------------------------
$ curl -X POST api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad/_execute -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
$ curl -X POST api/actions/connector/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad/_execute -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"params": {
"documents": [
@ -83,6 +83,6 @@ The API returns the following:
}
]
},
"actionId": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
"connector_id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
}
--------------------------------------------------

View file

@ -1,23 +1,23 @@
[[actions-and-connectors-api-get]]
=== Get action API
=== Get connector API
++++
<titleabbrev>Get action API</titleabbrev>
<titleabbrev>Get connector API</titleabbrev>
++++
Retrieves an action by ID.
Retrieves a connector by ID.
[[actions-and-connectors-api-get-request]]
==== Request
`GET <kibana host>:<port>/api/actions/action/<id>`
`GET <kibana host>:<port>/api/actions/connector/<id>`
`GET <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
`GET <kibana host>:<port>/s/<space_id>/api/actions/connector/<id>`
[[actions-and-connectors-api-get-params]]
==== Path parameters
`id`::
(Required, string) The ID of the action.
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
@ -33,7 +33,7 @@ Retrieves an action by ID.
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
$ curl -X GET api/actions/connector/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
--------------------------------------------------
// KIBANA
@ -43,13 +43,13 @@ The API returns the following:
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-action",
"connector_type_id": ".index",
"name": "my-connector",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
"is_preconfigured": false
}
--------------------------------------------------

View file

@ -4,14 +4,14 @@
<titleabbrev>Get all actions API</titleabbrev>
++++
Retrieves all actions.
Retrieves all connectors.
[[actions-and-connectors-api-get-all-request]]
==== Request
`GET <kibana host>:<port>/api/actions`
`GET <kibana host>:<port>/api/actions/connectors`
`GET <kibana host>:<port>/s/<space_id>/api/actions`
`GET <kibana host>:<port>/s/<space_id>/api/actions/connectors`
[[actions-and-connectors-api-get-all-path-params]]
==== Path parameters
@ -30,7 +30,7 @@ Retrieves all actions.
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions
$ curl -X GET api/actions/connectors
--------------------------------------------------
// KIBANA
@ -40,21 +40,23 @@ The API returns the following:
--------------------------------------------------
[
{
"id": "preconfigured-mail-action",
"actionTypeId": ".email",
"name": "email: preconfigured-mail-action",
"isPreconfigured": true
"id": "preconfigured-mail-connector",
"connector_type_id": ".email",
"name": "email: preconfigured-mail-connector",
"is_preconfigured": true,
"referenced_by_count": 1
},
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-action",
"connector_type_id": ".index",
"name": "my-connector",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
"is_preconfigured": false,
"referenced_by_count": 3
}
]
--------------------------------------------------

View file

@ -0,0 +1,82 @@
[[actions-and-connectors-legacy-api-create]]
==== Legacy Create connector API
++++
<titleabbrev>Legacy Create connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-create>> instead.
Creates a connector.
[[actions-and-connectors-legacy-api-create-request]]
===== Request
`POST <kibana host>:<port>/api/actions/action`
`POST <kibana host>:<port>/s/<space_id>/api/actions/action`
[[actions-and-connectors-legacy-api-create-path-params]]
===== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-create-request-body]]
===== Request body
`name`::
(Required, string) The display name for the connector.
`actionTypeId`::
(Required, string) The connector type ID for the connector.
`config`::
(Required, object) The configuration for the connector. Configuration properties vary depending on
the connector type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
`secrets`::
(Required, object) The secrets configuration for the connector. Secrets configuration properties vary
depending on the connector type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
+
WARNING: Remember these values. You must provide them each time you call the <<actions-and-connectors-legacy-api-update, update>> API.
[[actions-and-connectors-legacy-api-create-request-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-create-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X POST api/actions/action -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"name": "my-connector",
"actionTypeId": ".index",
"config": {
"index": "test-index"
}
}'
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-connector",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
}
--------------------------------------------------

View file

@ -0,0 +1,43 @@
[[actions-and-connectors-legacy-api-delete]]
==== Legacy Delete connector API
++++
<titleabbrev>Legacy Delete connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-delete>> instead.
Deletes a connector by ID.
WARNING: When you delete an connector, _it cannot be recovered_.
[[actions-and-connectors-legacy-api-delete-request]]
===== Request
`DELETE <kibana host>:<port>/api/actions/action/<id>`
`DELETE <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
[[actions-and-connectors-legacy-api-delete-path-params]]
===== Path parameters
`id`::
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-delete-response-codes]]
===== Response code
`200`::
Indicates a successful call.
===== Example
[source,sh]
--------------------------------------------------
$ curl -X DELETE api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
--------------------------------------------------
// KIBANA

View file

@ -0,0 +1,92 @@
[[actions-and-connectors-legacy-api-execute]]
==== Legacy Execute connector API
++++
<titleabbrev>Legacy Execute connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-execute>> instead.
Executes a connector by ID.
[[actions-and-connectors-legacy-api-execute-request]]
===== Request
`POST <kibana host>:<port>/api/actions/action/<id>/_execute`
`POST <kibana host>:<port>/s/<space_id>/api/actions/action/<id>/_execute`
[[actions-and-connectors-legacy-api-execute-params]]
===== Path parameters
`id`::
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-execute-request-body]]
===== Request body
`params`::
(Required, object) The parameters of the connector. Parameter properties vary depending on
the connector type. For information about the parameter properties, refer to <<action-types,Action and connector types>>.
[[actions-and-connectors-legacy-api-execute-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-execute-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X POST api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad/_execute -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"params": {
"documents": [
{
"id": "test_doc_id",
"name": "test_doc_name",
"message": "hello, world"
}
]
}
}'
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
{
"status": "ok",
"data": {
"took": 197,
"errors": false,
"items": [
{
"index": {
"_index": "updated-index",
"_id": "iKyijHcBKCsmXNFrQe3T",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
}
]
},
"actionId": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
}
--------------------------------------------------

View file

@ -0,0 +1,59 @@
[[actions-and-connectors-legacy-api-get]]
==== Legacy Get connector API
++++
<titleabbrev>Legacy Get connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-get>> instead.
Retrieves a connector by ID.
[[actions-and-connectors-legacy-api-get-request]]
===== Request
`GET <kibana host>:<port>/api/actions/action/<id>`
`GET <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
[[actions-and-connectors-legacy-api-get-params]]
===== Path parameters
`id`::
(Required, string) The ID of the action.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-get-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-get-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-connector",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
}
--------------------------------------------------

View file

@ -0,0 +1,64 @@
[[actions-and-connectors-legacy-api-get-all]]
==== Legacy Get all connector API
++++
<titleabbrev>Legacy Get all connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-get-all>> instead.
Retrieves all connectors.
[[actions-and-connectors-legacy-api-get-all-request]]
===== Request
`GET <kibana host>:<port>/api/actions`
`GET <kibana host>:<port>/s/<space_id>/api/actions`
[[actions-and-connectors-legacy-api-get-all-path-params]]
===== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-get-all-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-get-all-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
[
{
"id": "preconfigured-mail-action",
"actionTypeId": ".email",
"name": "email: preconfigured-mail-action",
"isPreconfigured": true
},
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "my-action",
"config": {
"index": "test-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
}
]
--------------------------------------------------

View file

@ -0,0 +1,4 @@
[[actions-and-connectors-legacy-apis]]
=== Deprecated 7.x APIs
These APIs are deprecated and will be removed as of 8.0.

View file

@ -0,0 +1,71 @@
[[actions-and-connectors-legacy-api-list]]
==== Legacy List connector types API
++++
<titleabbrev>Legacy List all connector types API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-list>> instead.
Retrieves a list of all connector types.
[[actions-and-connectors-legacy-api-list-request]]
===== Request
`GET <kibana host>:<port>/api/actions/list_action_types`
`GET <kibana host>:<port>/s/<space_id>/api/actions/list_action_types`
[[actions-and-connectors-legacy-api-list-path-params]]
===== Path parameters
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-list-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-list-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions/list_action_types
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
[
{
"id": ".email", <1>
"name": "Email", <2>
"minimumLicenseRequired": "gold", <3>
"enabled": false, <4>
"enabledInConfig": true, <5>
"enabledInLicense": false <6>
},
{
"id": ".index",
"name": "Index",
"minimumLicenseRequired": "basic",
"enabled": true,
"enabledInConfig": true,
"enabledInLicense": true
}
]
--------------------------------------------------
<1> `id` - The unique ID of the connector type.
<2> `name` - The name of the connector type.
<3> `minimumLicenseRequired` - The license required to use the connector type.
<4> `enabled` - Specifies if the connector type is enabled or disabled in {kib}.
<5> `enabledInConfig` - Specifies if the connector type is enabled or enabled in the {kib} .yml file.
<6> `enabledInLicense` - Specifies if the connector type is enabled or disabled in the license.

View file

@ -0,0 +1,77 @@
[[actions-and-connectors-legacy-api-update]]
==== Legacy Update connector API
++++
<titleabbrev>Legacy Update connector API</titleabbrev>
++++
deprecated::[7.13.0]
Please use the <<actions-and-connectors-api-update>> instead.
Updates the attributes for an existing connector.
[[actions-and-connectors-legacy-api-update-request]]
===== Request
`PUT <kibana host>:<port>/api/actions/action/<id>`
`PUT <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
[[actions-and-connectors-legacy-api-update-params]]
===== Path parameters
`id`::
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
[[actions-and-connectors-legacy-api-update-request-body]]
===== Request body
`name`::
(Required, string) The new name of the connector.
`config`::
(Required, object) The new connector configuration. Configuration properties vary depending on the connector type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
`secrets`::
(Required, object) The updated secrets configuration for the connector. Secrets properties vary depending on the connector type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
[[actions-and-connectors-legacy-api-update-codes]]
===== Response code
`200`::
Indicates a successful call.
[[actions-and-connectors-legacy-api-update-example]]
===== Example
[source,sh]
--------------------------------------------------
$ curl -X PUT api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"name": "updated-connector",
"config": {
"index": "updated-index"
}
}'
--------------------------------------------------
// KIBANA
The API returns the following:
[source,sh]
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "updated-connector",
"config": {
"index": "updated-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
}
--------------------------------------------------

View file

@ -1,17 +1,17 @@
[[actions-and-connectors-api-list]]
=== List action types API
=== List connector types API
++++
<titleabbrev>List all action types API</titleabbrev>
<titleabbrev>List all connector types API</titleabbrev>
++++
Retrieves a list of all action types.
Retrieves a list of all connector types.
[[actions-and-connectors-api-list-request]]
==== Request
`GET <kibana host>:<port>/api/actions/list_action_types`
`GET <kibana host>:<port>/api/actions/connector_types`
`GET <kibana host>:<port>/s/<space_id>/api/actions/list_action_types`
`GET <kibana host>:<port>/s/<space_id>/api/actions/connector_types`
[[actions-and-connectors-api-list-path-params]]
==== Path parameters
@ -30,7 +30,7 @@ Retrieves a list of all action types.
[source,sh]
--------------------------------------------------
$ curl -X GET api/actions/list_action_types
$ curl -X GET api/actions/connector_types
--------------------------------------------------
// KIBANA
@ -42,26 +42,26 @@ The API returns the following:
{
"id": ".email", <1>
"name": "Email", <2>
"minimumLicenseRequired": "gold", <3>
"minimum_license_required": "gold", <3>
"enabled": false, <4>
"enabledInConfig": true, <5>
"enabledInLicense": false <6>
"enabled_in_config": true, <5>
"enabled_in_license": false <6>
},
{
"id": ".index",
"name": "Index",
"minimumLicenseRequired": "basic",
"minimum_license_required": "basic",
"enabled": true,
"enabledInConfig": true,
"enabledInLicense": true
"enabled_in_config": true,
"enabled_in_license": true
}
]
--------------------------------------------------
<1> `id` - The unique ID of the action type.
<2> `name` - The name of the action type.
<3> `minimumLicenseRequired` - The license required to use the action type.
<4> `enabled` - Specifies if the action type is enabled or disabled in {kib}.
<5> `enabledInConfig` - Specifies if the action type is enabled or enabled in the {kib} .yml file.
<6> `enabledInLicense` - Specifies if the action type is enabled or disabled in the license.
<1> `id` - The unique ID of the connector type.
<2> `name` - The name of the connector type.
<3> `minimum_license_required` - The license required to use the connector type.
<4> `enabled` - Specifies if the connector type is enabled or disabled in {kib}.
<5> `enabled_in_config` - Specifies if the connector type is enabled or enabled in the {kib} .yml file.
<6> `enabled_in_license` - Specifies if the connector type is enabled or disabled in the license.

View file

@ -1,23 +1,23 @@
[[actions-and-connectors-api-update]]
=== Update action API
=== Update connector API
++++
<titleabbrev>Update action API</titleabbrev>
<titleabbrev>Update connector API</titleabbrev>
++++
Updates the attributes for an existing action.
Updates the attributes for an existing connector.
[[actions-and-connectors-api-update-request]]
==== Request
`PUT <kibana host>:<port>/api/actions/action/<id>`
`PUT <kibana host>:<port>/api/actions/connector/<id>`
`PUT <kibana host>:<port>/s/<space_id>/api/actions/action/<id>`
`PUT <kibana host>:<port>/s/<space_id>/api/actions/connector/<id>`
[[actions-and-connectors-api-update-params]]
==== Path parameters
`id`::
(Required, string) The ID of the action.
(Required, string) The ID of the connector.
`space_id`::
(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
@ -26,13 +26,13 @@ Updates the attributes for an existing action.
==== Request body
`name`::
(Required, string) The new name of the action.
(Required, string) The new name of the connector.
`config`::
(Required, object) The new action configuration. Configuration properties vary depending on the action type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
(Required, object) The new connector configuration. Configuration properties vary depending on the connector type. For information about the configuration properties, refer to <<action-types,Action and connector types>>.
`secrets`::
(Required, object) The updated secrets configuration for the action. Secrets properties vary depending on the action type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
(Required, object) The updated secrets configuration for the connector. Secrets properties vary depending on the connector type. For information about the secrets configuration properties, refer to <<action-types,Action and connector types>>.
[[actions-and-connectors-api-update-codes]]
==== Response code
@ -45,9 +45,9 @@ Updates the attributes for an existing action.
[source,sh]
--------------------------------------------------
$ curl -X PUT api/actions/action/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
$ curl -X PUT api/actions/connector/c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
"name": "updated-action",
"name": "updated-connector",
"config": {
"index": "updated-index"
}
@ -61,13 +61,13 @@ The API returns the following:
--------------------------------------------------
{
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
"actionTypeId": ".index",
"name": "updated-action",
"connector_type_id": ".index",
"name": "updated-connector",
"config": {
"index": "updated-index",
"refresh": false,
"executionTimeField": null
},
"isPreconfigured": false
"is_preconfigured": false
}
--------------------------------------------------

View file

@ -56,7 +56,7 @@ interface Action extends ActionUpdate {
actionTypeId: string;
}
interface CreateOptions {
export interface CreateOptions {
action: Action;
}
@ -73,7 +73,7 @@ interface ConstructorOptions {
auditLogger?: AuditLogger;
}
interface UpdateOptions {
export interface UpdateOptions {
id: string;
action: ActionUpdate;
}

View file

@ -50,15 +50,7 @@ import {
import { getActionsConfigurationUtilities } from './actions_config';
import {
createActionRoute,
deleteActionRoute,
getAllActionRoute,
getActionRoute,
updateActionRoute,
listActionTypesRoute,
executeActionRoute,
} from './routes';
import { defineRoutes } from './routes';
import { IEventLogger, IEventLogService } from '../../event_log/server';
import { initializeActionsTelemetry, scheduleActionsTelemetry } from './usage/task';
import {
@ -237,14 +229,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
}
// Routes
const router = core.http.createRouter<ActionsRequestHandlerContext>();
createActionRoute(router, this.licenseState);
deleteActionRoute(router, this.licenseState);
getActionRoute(router, this.licenseState);
getAllActionRoute(router, this.licenseState);
updateActionRoute(router, this.licenseState);
listActionTypesRoute(router, this.licenseState);
executeActionRoute(router, this.licenseState);
defineRoutes(core.http.createRouter<ActionsRequestHandlerContext>(), this.licenseState);
return {
registerType: <

View file

@ -0,0 +1,157 @@
/*
* 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 { connectorTypesRoute } from './connector_types';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { LicenseType } from '../../../../plugins/licensing/server';
import { actionsClientMock } from '../mocks';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('connectorTypesRoute', () => {
it('lists action types with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
connectorTypesRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`);
const listTypes = [
{
id: '1',
name: 'name',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'gold' as LicenseType,
},
];
const actionsClient = actionsClientMock.create();
actionsClient.listTypes.mockResolvedValueOnce(listTypes);
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Array [
Object {
"enabled": true,
"enabled_in_config": true,
"enabled_in_license": true,
"id": "1",
"minimum_license_required": "gold",
"name": "name",
},
],
}
`);
expect(res.ok).toHaveBeenCalledWith({
body: [
{
id: '1',
name: 'name',
enabled: true,
enabled_in_config: true,
enabled_in_license: true,
minimum_license_required: 'gold',
},
],
});
});
it('ensures the license allows listing action types', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
connectorTypesRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`);
const listTypes = [
{
id: '1',
name: 'name',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'gold' as LicenseType,
},
];
const actionsClient = actionsClientMock.create();
actionsClient.listTypes.mockResolvedValueOnce(listTypes);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
},
['ok']
);
await handler(context, req, res);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents listing action types', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
connectorTypesRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`);
const listTypes = [
{
id: '1',
name: 'name',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'gold' as LicenseType,
},
];
const actionsClient = actionsClientMock.create();
actionsClient.listTypes.mockResolvedValueOnce(listTypes);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
},
['ok']
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -0,0 +1,42 @@
/*
* 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 { IRouter } from 'kibana/server';
import { ILicenseState } from '../lib';
import { ActionType, BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteResponseCase } from './rewrite_request_case';
const rewriteBodyRes: RewriteResponseCase<ActionType[]> = (results) => {
return results.map(({ enabledInConfig, enabledInLicense, minimumLicenseRequired, ...res }) => ({
...res,
enabled_in_config: enabledInConfig,
enabled_in_license: enabledInLicense,
minimum_license_required: minimumLicenseRequired,
}));
};
export const connectorTypesRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ACTION_API_PATH}/connector_types`,
validate: {},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
return res.ok({
body: rewriteBodyRes(await actionsClient.listTypes()),
});
})
)
);
};

View file

@ -8,16 +8,18 @@
import { createActionRoute } from './create';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess, ActionTypeDisabledError } from '../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../actions_client.mock';
import { verifyAccessAndContext } from './verify_access_and_context';
import { omit } from 'lodash';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('createActionRoute', () => {
@ -29,7 +31,7 @@ describe('createActionRoute', () => {
const [config, handler] = router.post.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector"`);
const createResult = {
id: '1',
@ -39,6 +41,12 @@ describe('createActionRoute', () => {
isPreconfigured: false,
};
const createApiResult = {
...omit(createResult, ['actionTypeId', 'isPreconfigured']),
connector_type_id: createResult.actionTypeId,
is_preconfigured: createResult.isPreconfigured,
};
const actionsClient = actionsClientMock.create();
actionsClient.create.mockResolvedValueOnce(createResult);
@ -47,7 +55,7 @@ describe('createActionRoute', () => {
{
body: {
name: 'My name',
actionTypeId: 'abc',
connector_type_id: 'abc',
config: { foo: true },
secrets: {},
},
@ -55,7 +63,7 @@ describe('createActionRoute', () => {
['ok']
);
expect(await handler(context, req, res)).toEqual({ body: createResult });
expect(await handler(context, req, res)).toEqual({ body: createApiResult });
expect(actionsClient.create).toHaveBeenCalledTimes(1);
expect(actionsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
@ -74,7 +82,7 @@ describe('createActionRoute', () => {
`);
expect(res.ok).toHaveBeenCalledWith({
body: createResult,
body: createApiResult,
});
});
@ -95,18 +103,28 @@ describe('createActionRoute', () => {
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {
name: 'My name',
connector_type_id: 'abc',
config: { foo: true },
secrets: {},
},
}
);
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents creating actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -123,28 +141,18 @@ describe('createActionRoute', () => {
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {
name: 'My name',
connector_type_id: 'abc',
config: { foo: true },
secrets: {},
},
}
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the action type gets validated for the license', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
createActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.create.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok', 'forbidden']);
await handler(context, req, res);
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
});
});

View file

@ -8,46 +8,54 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ActionResult, ActionsRequestHandlerContext } from '../types';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib';
import { ILicenseState } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteRequestCase, RewriteResponseCase } from './rewrite_request_case';
import { CreateOptions } from '../actions_client';
export const bodySchema = schema.object({
name: schema.string(),
actionTypeId: schema.string(),
connector_type_id: schema.string(),
config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
});
const rewriteBodyReq: RewriteRequestCase<CreateOptions['action']> = ({
connector_type_id: actionTypeId,
name,
config,
secrets,
}) => ({ actionTypeId, name, config, secrets });
const rewriteBodyRes: RewriteResponseCase<ActionResult> = ({
actionTypeId,
isPreconfigured,
...res
}) => ({
...res,
connector_type_id: actionTypeId,
is_preconfigured: isPreconfigured,
});
export const createActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.post(
{
path: `${BASE_ACTION_API_PATH}/action`,
path: `${BASE_ACTION_API_PATH}/connector`,
validate: {
body: bodySchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const action = req.body;
try {
const actionRes: ActionResult = await actionsClient.create({ action });
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const action = rewriteBodyReq(req.body);
return res.ok({
body: actionRes,
body: rewriteBodyRes(await actionsClient.create({ action })),
});
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
})
)
);
};

View file

@ -8,16 +8,17 @@
import { deleteActionRoute } from './delete';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess } from '../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../mocks';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('deleteActionRoute', () => {
@ -29,7 +30,7 @@ describe('deleteActionRoute', () => {
const [config, handler] = router.delete.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector/{id}"`);
const actionsClient = actionsClientMock.create();
actionsClient.delete.mockResolvedValueOnce({});
@ -78,14 +79,14 @@ describe('deleteActionRoute', () => {
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -105,6 +106,6 @@ describe('deleteActionRoute', () => {
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -7,9 +7,10 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib';
import { ILicenseState } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
const paramSchema = schema.object({
id: schema.string(),
@ -21,27 +22,18 @@ export const deleteActionRoute = (
) => {
router.delete(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
path: `${BASE_ACTION_API_PATH}/connector/{id}`,
validate: {
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
try {
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
await actionsClient.delete({ id });
return res.noContent();
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
})
)
);
};

View file

@ -8,17 +8,19 @@
import { executeActionRoute } from './execute';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../lib';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { asHttpRequestExecutionSource } from '../lib';
import { actionsClientMock } from '../actions_client.mock';
import { ActionTypeExecutorResult } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('executeActionRoute', () => {
@ -45,7 +47,7 @@ describe('executeActionRoute', () => {
);
const executeResult = {
actionId: '1',
connector_id: '1',
status: 'ok',
};
@ -53,7 +55,7 @@ describe('executeActionRoute', () => {
const [config, handler] = router.post.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}/_execute"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector/{id}/_execute"`);
expect(await handler(context, req, res)).toEqual({ body: executeResult });
@ -131,7 +133,7 @@ describe('executeActionRoute', () => {
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents action execution', async () => {
@ -144,7 +146,7 @@ describe('executeActionRoute', () => {
status: 'ok',
});
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -163,31 +165,6 @@ describe('executeActionRoute', () => {
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the action type gets validated for the license', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {},
params: {},
},
['ok', 'forbidden']
);
executeActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
await handler(context, req, res);
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -7,11 +7,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib';
import { ILicenseState } from '../lib';
import { ActionTypeExecutorResult, ActionsRequestHandlerContext } from '../types';
import { BASE_ACTION_API_PATH } from '../../common';
import { asHttpRequestExecutionSource } from '../lib/action_execution_source';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteResponseCase } from './rewrite_request_case';
const paramSchema = schema.object({
id: schema.string(),
@ -21,29 +23,33 @@ const bodySchema = schema.object({
params: schema.recordOf(schema.string(), schema.any()),
});
const rewriteBodyRes: RewriteResponseCase<ActionTypeExecutorResult<unknown>> = ({
actionId,
serviceMessage,
...res
}) => ({
...res,
connector_id: actionId,
...(serviceMessage ? { service_message: serviceMessage } : {}),
});
export const executeActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.post(
{
path: `${BASE_ACTION_API_PATH}/action/{id}/_execute`,
path: `${BASE_ACTION_API_PATH}/connector/{id}/_execute`,
validate: {
body: bodySchema,
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { params } = req.body;
const { id } = req.params;
try {
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const { params } = req.body;
const { id } = req.params;
const body: ActionTypeExecutorResult<unknown> = await actionsClient.execute({
params,
actionId: id,
@ -51,15 +57,10 @@ export const executeActionRoute = (
});
return body
? res.ok({
body,
body: rewriteBodyRes(body),
})
: res.noContent();
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
})
)
);
};

View file

@ -8,16 +8,17 @@
import { getActionRoute } from './get';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess } from '../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../actions_client.mock';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('getActionRoute', () => {
@ -29,7 +30,7 @@ describe('getActionRoute', () => {
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector/{id}"`);
const getResult = {
id: '1',
@ -53,10 +54,10 @@ describe('getActionRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"actionTypeId": "2",
"config": Object {},
"connector_type_id": "2",
"id": "1",
"isPreconfigured": false,
"is_preconfigured": false,
"name": "action name",
},
}
@ -66,7 +67,13 @@ describe('getActionRoute', () => {
expect(actionsClient.get.mock.calls[0][0].id).toEqual('1');
expect(res.ok).toHaveBeenCalledWith({
body: getResult,
body: {
id: '1',
connector_type_id: '2',
name: 'action name',
config: {},
is_preconfigured: false,
},
});
});
@ -97,14 +104,14 @@ describe('getActionRoute', () => {
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents getting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -131,6 +138,6 @@ describe('getActionRoute', () => {
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -7,35 +7,45 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess } from '../lib';
import { ILicenseState } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { ActionResult, ActionsRequestHandlerContext } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteResponseCase } from './rewrite_request_case';
const paramSchema = schema.object({
id: schema.string(),
});
const rewriteBodyRes: RewriteResponseCase<ActionResult> = ({
actionTypeId,
isPreconfigured,
...res
}) => ({
...res,
connector_type_id: actionTypeId,
is_preconfigured: isPreconfigured,
});
export const getActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
path: `${BASE_ACTION_API_PATH}/connector/{id}`,
validate: {
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
return res.ok({
body: await actionsClient.get({ id }),
});
})
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
return res.ok({
body: rewriteBodyRes(await actionsClient.get({ id })),
});
})
)
);
};

View file

@ -8,16 +8,17 @@
import { getAllActionRoute } from './get_all';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess } from '../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../actions_client.mock';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('getAllActionRoute', () => {
@ -29,7 +30,7 @@ describe('getAllActionRoute', () => {
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
@ -57,7 +58,7 @@ describe('getAllActionRoute', () => {
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
@ -66,14 +67,14 @@ describe('getAllActionRoute', () => {
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents getting all actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -81,7 +82,7 @@ describe('getAllActionRoute', () => {
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
@ -90,6 +91,6 @@ describe('getAllActionRoute', () => {
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -6,9 +6,20 @@
*/
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess } from '../lib';
import { ILicenseState } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { ActionsRequestHandlerContext, FindActionResult } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteResponseCase } from './rewrite_request_case';
const rewriteBodyRes: RewriteResponseCase<FindActionResult[]> = (results) => {
return results.map(({ actionTypeId, isPreconfigured, referencedByCount, ...res }) => ({
...res,
connector_type_id: actionTypeId,
is_preconfigured: isPreconfigured,
referenced_by_count: referencedByCount,
}));
};
export const getAllActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
@ -16,19 +27,17 @@ export const getAllActionRoute = (
) => {
router.get(
{
path: `${BASE_ACTION_API_PATH}`,
path: `${BASE_ACTION_API_PATH}/connectors`,
validate: {},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const result = await actionsClient.getAll();
return res.ok({
body: result,
});
})
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const result = await actionsClient.getAll();
return res.ok({
body: rewriteBodyRes(result),
});
})
)
);
};

View file

@ -5,10 +5,29 @@
* 2.0.
*/
export { createActionRoute } from './create';
export { deleteActionRoute } from './delete';
export { getAllActionRoute } from './get_all';
export { getActionRoute } from './get';
export { updateActionRoute } from './update';
export { listActionTypesRoute } from './list_action_types';
export { executeActionRoute } from './execute';
import { IRouter } from 'kibana/server';
import { ILicenseState } from '../lib';
import { ActionsRequestHandlerContext } from '../types';
import { createActionRoute } from './create';
import { deleteActionRoute } from './delete';
import { executeActionRoute } from './execute';
import { getActionRoute } from './get';
import { getAllActionRoute } from './get_all';
import { connectorTypesRoute } from './connector_types';
import { updateActionRoute } from './update';
import { defineLegacyRoutes } from './legacy';
export function defineRoutes(
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) {
defineLegacyRoutes(router, licenseState);
createActionRoute(router, licenseState);
deleteActionRoute(router, licenseState);
getActionRoute(router, licenseState);
getAllActionRoute(router, licenseState);
updateActionRoute(router, licenseState);
connectorTypesRoute(router, licenseState);
executeActionRoute(router, licenseState);
}

View file

@ -5,12 +5,13 @@
* 2.0.
*/
import { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server';
import { KibanaRequest, KibanaResponseFactory } from 'kibana/server';
import { identity } from 'lodash';
import type { MethodKeysOf } from '@kbn/utility-types';
import { httpServerMock } from '../../../../../src/core/server/mocks';
import { ActionType } from '../../common';
import { ActionsClientMock, actionsClientMock } from '../actions_client.mock';
import { httpServerMock } from '../../../../../../src/core/server/mocks';
import { ActionType } from '../../../common';
import { ActionsClientMock, actionsClientMock } from '../../actions_client.mock';
import { ActionsRequestHandlerContext } from '../../types';
export function mockHandlerArguments(
{
@ -19,7 +20,7 @@ export function mockHandlerArguments(
}: { actionsClient?: ActionsClientMock; listTypes?: ActionType[] },
req: unknown,
res?: Array<MethodKeysOf<KibanaResponseFactory>>
): [RequestHandlerContext, KibanaRequest<unknown, unknown, unknown>, KibanaResponseFactory] {
): [ActionsRequestHandlerContext, KibanaRequest<unknown, unknown, unknown>, KibanaResponseFactory] {
const listTypes = jest.fn(() => listTypesRes);
return [
({
@ -37,7 +38,7 @@ export function mockHandlerArguments(
);
},
},
} as unknown) as RequestHandlerContext,
} as unknown) as ActionsRequestHandlerContext,
req as KibanaRequest<unknown, unknown, unknown>,
mockResponseFactory(res),
];

View file

@ -0,0 +1,131 @@
/*
* 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 { createActionRoute } from './create';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { actionsClientMock } from '../../actions_client.mock';
import { verifyAccessAndContext } from '../verify_access_and_context';
jest.mock('../verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('createActionRoute', () => {
it('creates an action with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
createActionRoute(router, licenseState);
const [config, handler] = router.post.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action"`);
const createResult = {
id: '1',
name: 'My name',
actionTypeId: 'abc',
config: { foo: true },
isPreconfigured: false,
};
const actionsClient = actionsClientMock.create();
actionsClient.create.mockResolvedValueOnce(createResult);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {
name: 'My name',
actionTypeId: 'abc',
config: { foo: true },
secrets: {},
},
},
['ok']
);
expect(await handler(context, req, res)).toEqual({ body: createResult });
expect(actionsClient.create).toHaveBeenCalledTimes(1);
expect(actionsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"action": Object {
"actionTypeId": "abc",
"config": Object {
"foo": true,
},
"name": "My name",
"secrets": Object {},
},
},
]
`);
expect(res.ok).toHaveBeenCalledWith({
body: createResult,
});
});
it('ensures the license allows creating actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
createActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.create.mockResolvedValueOnce({
id: '1',
name: 'My name',
actionTypeId: 'abc',
config: { foo: true },
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
await handler(context, req, res);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents creating actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
createActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.create.mockResolvedValueOnce({
id: '1',
name: 'My name',
actionTypeId: 'abc',
config: { foo: true },
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
});
});

View file

@ -0,0 +1,43 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ActionsRequestHandlerContext } from '../../types';
import { ILicenseState } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { verifyAccessAndContext } from '../verify_access_and_context';
export const bodySchema = schema.object({
name: schema.string(),
actionTypeId: schema.string(),
config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
});
export const createActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.post(
{
path: `${BASE_ACTION_API_PATH}/action`,
validate: {
body: bodySchema,
},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const action = req.body;
return res.ok({
body: await actionsClient.create({ action }),
});
})
)
);
};

View file

@ -0,0 +1,110 @@
/*
* 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 { deleteActionRoute } from './delete';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { verifyApiAccess } from '../../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { actionsClientMock } from '../../mocks';
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('deleteActionRoute', () => {
it('deletes an action with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
deleteActionRoute(router, licenseState);
const [config, handler] = router.delete.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
const actionsClient = actionsClientMock.create();
actionsClient.delete.mockResolvedValueOnce({});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: {
id: '1',
},
},
['noContent']
);
expect(await handler(context, req, res)).toEqual(undefined);
expect(actionsClient.delete).toHaveBeenCalledTimes(1);
expect(actionsClient.delete.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"id": "1",
},
]
`);
expect(res.noContent).toHaveBeenCalled();
});
it('ensures the license allows deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
deleteActionRoute(router, licenseState);
const [, handler] = router.delete.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.delete.mockResolvedValueOnce({});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
}
);
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
deleteActionRoute(router, licenseState);
const [, handler] = router.delete.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.delete.mockResolvedValueOnce({});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
id: '1',
}
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
});

View file

@ -0,0 +1,47 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { ActionsRequestHandlerContext } from '../../types';
const paramSchema = schema.object({
id: schema.string(),
});
export const deleteActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.delete(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
validate: {
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
try {
await actionsClient.delete({ id });
return res.noContent();
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
);
};

View file

@ -0,0 +1,193 @@
/*
* 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 { executeActionRoute } from './execute';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../../lib';
import { actionsClientMock } from '../../actions_client.mock';
import { ActionTypeExecutorResult } from '../../types';
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('executeActionRoute', () => {
it('executes an action with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockResolvedValueOnce({ status: 'ok', actionId: '1' });
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {
params: {
someData: 'data',
},
},
params: {
id: '1',
},
},
['ok']
);
const executeResult = {
actionId: '1',
status: 'ok',
};
executeActionRoute(router, licenseState);
const [config, handler] = router.post.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}/_execute"`);
expect(await handler(context, req, res)).toEqual({ body: executeResult });
expect(actionsClient.execute).toHaveBeenCalledWith({
actionId: '1',
params: {
someData: 'data',
},
source: asHttpRequestExecutionSource(req),
});
expect(res.ok).toHaveBeenCalled();
});
it('returns a "204 NO CONTENT" when the executor returns a nullish value', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockResolvedValueOnce(
(null as unknown) as ActionTypeExecutorResult<void>
);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {
params: {},
},
params: {
id: '1',
},
},
['noContent']
);
executeActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
expect(await handler(context, req, res)).toEqual(undefined);
expect(actionsClient.execute).toHaveBeenCalledWith({
actionId: '1',
params: {},
source: asHttpRequestExecutionSource(req),
});
expect(res.ok).not.toHaveBeenCalled();
expect(res.noContent).toHaveBeenCalled();
});
it('ensures the license allows action execution', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockResolvedValue({
actionId: '1',
status: 'ok',
});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {},
params: {},
},
['ok']
);
executeActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents action execution', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockResolvedValue({
actionId: '1',
status: 'ok',
});
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {},
params: {},
},
['ok']
);
executeActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the action type gets validated for the license', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.execute.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
body: {},
params: {},
},
['ok', 'forbidden']
);
executeActionRoute(router, licenseState);
const [, handler] = router.post.mock.calls[0];
await handler(context, req, res);
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
});
});

View file

@ -0,0 +1,65 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib';
import { ActionTypeExecutorResult, ActionsRequestHandlerContext } from '../../types';
import { BASE_ACTION_API_PATH } from '../../../common';
import { asHttpRequestExecutionSource } from '../../lib/action_execution_source';
const paramSchema = schema.object({
id: schema.string(),
});
const bodySchema = schema.object({
params: schema.recordOf(schema.string(), schema.any()),
});
export const executeActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.post(
{
path: `${BASE_ACTION_API_PATH}/action/{id}/_execute`,
validate: {
body: bodySchema,
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { params } = req.body;
const { id } = req.params;
try {
const body: ActionTypeExecutorResult<unknown> = await actionsClient.execute({
params,
actionId: id,
source: asHttpRequestExecutionSource(req),
});
return body
? res.ok({
body,
})
: res.noContent();
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
);
};

View file

@ -0,0 +1,136 @@
/*
* 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 { getActionRoute } from './get';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { verifyApiAccess } from '../../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { actionsClientMock } from '../../actions_client.mock';
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('getActionRoute', () => {
it('gets an action with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getActionRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
const getResult = {
id: '1',
actionTypeId: '2',
name: 'action name',
config: {},
isPreconfigured: false,
};
const actionsClient = actionsClientMock.create();
actionsClient.get.mockResolvedValueOnce(getResult);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
},
['ok']
);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"actionTypeId": "2",
"config": Object {},
"id": "1",
"isPreconfigured": false,
"name": "action name",
},
}
`);
expect(actionsClient.get).toHaveBeenCalledTimes(1);
expect(actionsClient.get.mock.calls[0][0].id).toEqual('1');
expect(res.ok).toHaveBeenCalledWith({
body: getResult,
});
});
it('ensures the license allows getting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getActionRoute(router, licenseState);
const [, handler] = router.get.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.get.mockResolvedValueOnce({
id: '1',
actionTypeId: '2',
name: 'action name',
config: {},
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
},
['ok']
);
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents getting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
getActionRoute(router, licenseState);
const [, handler] = router.get.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.get.mockResolvedValueOnce({
id: '1',
actionTypeId: '2',
name: 'action name',
config: {},
isPreconfigured: false,
});
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: { id: '1' },
},
['ok']
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
});

View file

@ -0,0 +1,41 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { ActionsRequestHandlerContext } from '../../types';
const paramSchema = schema.object({
id: schema.string(),
});
export const getActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
validate: {
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
return res.ok({
body: await actionsClient.get({ id }),
});
})
);
};

View file

@ -0,0 +1,95 @@
/*
* 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 { getAllActionRoute } from './get_all';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { verifyApiAccess } from '../../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { actionsClientMock } from '../../actions_client.mock';
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('getAllActionRoute', () => {
it('get all actions with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getAllActionRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Array [],
}
`);
expect(actionsClient.getAll).toHaveBeenCalledTimes(1);
expect(res.ok).toHaveBeenCalledWith({
body: [],
});
});
it('ensures the license allows getting all actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getAllActionRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents getting all actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
getAllActionRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions"`);
const actionsClient = actionsClientMock.create();
actionsClient.getAll.mockResolvedValueOnce([]);
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
});

View file

@ -0,0 +1,34 @@
/*
* 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 { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { ActionsRequestHandlerContext } from '../../types';
export const getAllActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ACTION_API_PATH}`,
validate: {},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const result = await actionsClient.getAll();
return res.ok({
body: result,
});
})
);
};

View file

@ -0,0 +1,30 @@
/*
* 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 { IRouter } from 'kibana/server';
import { ILicenseState } from '../../lib';
import { ActionsRequestHandlerContext } from '../../types';
import { createActionRoute } from './create';
import { deleteActionRoute } from './delete';
import { getAllActionRoute } from './get_all';
import { getActionRoute } from './get';
import { updateActionRoute } from './update';
import { listActionTypesRoute } from './list_action_types';
import { executeActionRoute } from './execute';
export function defineLegacyRoutes(
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) {
createActionRoute(router, licenseState);
deleteActionRoute(router, licenseState);
getActionRoute(router, licenseState);
getAllActionRoute(router, licenseState);
updateActionRoute(router, licenseState);
listActionTypesRoute(router, licenseState);
executeActionRoute(router, licenseState);
}

View file

@ -7,13 +7,13 @@
import { listActionTypesRoute } from './list_action_types';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess } from '../lib';
import { licenseStateMock } from '../../lib/license_state.mock';
import { verifyApiAccess } from '../../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { LicenseType } from '../../../../plugins/licensing/server';
import { actionsClientMock } from '../mocks';
import { LicenseType } from '../../../../../plugins/licensing/server';
import { actionsClientMock } from '../../mocks';
jest.mock('../lib/verify_api_access.ts', () => ({
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));

View file

@ -6,9 +6,9 @@
*/
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { ILicenseState, verifyApiAccess } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { ActionsRequestHandlerContext } from '../../types';
export const listActionTypesRoute = (
router: IRouter<ActionsRequestHandlerContext>,

View file

@ -0,0 +1,185 @@
/*
* 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 { updateActionRoute } from './update';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../../lib/license_state.mock';
import { verifyApiAccess, ActionTypeDisabledError } from '../../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { actionsClientMock } from '../../actions_client.mock';
jest.mock('../../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('updateActionRoute', () => {
it('updates an action with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
updateActionRoute(router, licenseState);
const [config, handler] = router.put.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
const updateResult = {
id: '1',
actionTypeId: 'my-action-type-id',
name: 'My name',
config: { foo: true },
isPreconfigured: false,
};
const actionsClient = actionsClientMock.create();
actionsClient.update.mockResolvedValueOnce(updateResult);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: {
id: '1',
},
body: {
name: 'My name',
config: { foo: true },
secrets: { key: 'i8oh34yf9783y39' },
},
},
['ok']
);
expect(await handler(context, req, res)).toEqual({ body: updateResult });
expect(actionsClient.update).toHaveBeenCalledTimes(1);
expect(actionsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"action": Object {
"config": Object {
"foo": true,
},
"name": "My name",
"secrets": Object {
"key": "i8oh34yf9783y39",
},
},
"id": "1",
},
]
`);
expect(res.ok).toHaveBeenCalled();
});
it('ensures the license allows deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
updateActionRoute(router, licenseState);
const [, handler] = router.put.mock.calls[0];
const updateResult = {
id: '1',
actionTypeId: 'my-action-type-id',
name: 'My name',
config: { foo: true },
isPreconfigured: false,
};
const actionsClient = actionsClientMock.create();
actionsClient.update.mockResolvedValueOnce(updateResult);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: {
id: '1',
},
body: {
name: 'My name',
config: { foo: true },
secrets: { key: 'i8oh34yf9783y39' },
},
},
['ok']
);
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
updateActionRoute(router, licenseState);
const [, handler] = router.put.mock.calls[0];
const updateResult = {
id: '1',
actionTypeId: 'my-action-type-id',
name: 'My name',
config: { foo: true },
isPreconfigured: false,
};
const actionsClient = actionsClientMock.create();
actionsClient.update.mockResolvedValueOnce(updateResult);
const [context, req, res] = mockHandlerArguments(
{ actionsClient },
{
params: {
id: '1',
},
body: {
name: 'My name',
config: { foo: true },
secrets: { key: 'i8oh34yf9783y39' },
},
},
['ok']
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the action type gets validated for the license', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
updateActionRoute(router, licenseState);
const [, handler] = router.put.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.update.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
const [context, req, res] = mockHandlerArguments({ actionsClient }, { params: {}, body: {} }, [
'ok',
'forbidden',
]);
await handler(context, req, res);
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
});
});

View file

@ -0,0 +1,60 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../../lib';
import { BASE_ACTION_API_PATH } from '../../../common';
import { ActionsRequestHandlerContext } from '../../types';
const paramSchema = schema.object({
id: schema.string(),
});
const bodySchema = schema.object({
name: schema.string(),
config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
});
export const updateActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.put(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
validate: {
body: bodySchema,
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
const { name, config, secrets } = req.body;
try {
return res.ok({
body: await actionsClient.update({
id,
action: { name, config, secrets },
}),
});
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
);
};

View file

@ -0,0 +1,48 @@
/*
* 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.
*/
type RenameActionToConnector<K extends string> = K extends `actionTypeId`
? `connectorTypeId`
: K extends `actionId`
? `connectorId`
: K;
export type AsApiContract<T> = {
[K in keyof T as CamelToSnake<RenameActionToConnector<Extract<K, string>>>]: T[K];
};
export type RewriteRequestCase<T> = (requested: AsApiContract<T>) => T;
export type RewriteResponseCase<T> = (
responded: T
) => T extends Array<infer Item> ? Array<AsApiContract<Item>> : AsApiContract<T>;
/**
* This type maps Camel Case strings into their Snake Case version.
* This is achieved by checking each character and, if it is an uppercase character, it is mapped to an
* underscore followed by a lowercase one.
*
* The reason there are two ternaries is that, for perfformance reasons, TS limits its
* character parsing to ~15 characters.
* To get around this we use the second turnery to parse 2 characters at a time, which allows us to support
* strings that are 30 characters long.
*
* If you get the TS #2589 error ("Type instantiation is excessively deep and possibly infinite") then most
* likely you have a string that's longer than 30 characters.
* Address this by reducing the length if possible, otherwise, you'll need to add a 3rd ternary which
* parses 3 chars at a time :grimace:
*
* For more details see this PR comment: https://github.com/microsoft/TypeScript/pull/40336#issuecomment-686723087
*/
type CamelToSnake<T extends string> = string extends T
? string
: T extends `${infer C0}${infer C1}${infer R}`
? `${C0 extends Uppercase<C0> ? '_' : ''}${Lowercase<C0>}${C1 extends Uppercase<C1>
? '_'
: ''}${Lowercase<C1>}${CamelToSnake<R>}`
: T extends `${infer C0}${infer R}`
? `${C0 extends Uppercase<C0> ? '_' : ''}${Lowercase<C0>}${CamelToSnake<R>}`
: '';

View file

@ -8,16 +8,17 @@
import { updateActionRoute } from './update';
import { httpServiceMock } from 'src/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess, ActionTypeDisabledError } from '../lib';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../actions_client.mock';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
jest.mock('./verify_access_and_context.ts', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('updateActionRoute', () => {
@ -29,7 +30,7 @@ describe('updateActionRoute', () => {
const [config, handler] = router.put.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(`"/api/actions/action/{id}"`);
expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector/{id}"`);
const updateResult = {
id: '1',
@ -57,7 +58,15 @@ describe('updateActionRoute', () => {
['ok']
);
expect(await handler(context, req, res)).toEqual({ body: updateResult });
expect(await handler(context, req, res)).toEqual({
body: {
id: '1',
connector_type_id: 'my-action-type-id',
name: 'My name',
config: { foo: true },
is_preconfigured: false,
},
});
expect(actionsClient.update).toHaveBeenCalledTimes(1);
expect(actionsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
@ -116,14 +125,14 @@ describe('updateActionRoute', () => {
await handler(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents deleting actions', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
@ -159,27 +168,6 @@ describe('updateActionRoute', () => {
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the action type gets validated for the license', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
updateActionRoute(router, licenseState);
const [, handler] = router.put.mock.calls[0];
const actionsClient = actionsClientMock.create();
actionsClient.update.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
const [context, req, res] = mockHandlerArguments({ actionsClient }, { params: {}, body: {} }, [
'ok',
'forbidden',
]);
await handler(context, req, res);
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -7,9 +7,11 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from 'kibana/server';
import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib';
import { ILicenseState } from '../lib';
import { BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { ActionResult, ActionsRequestHandlerContext } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
import { RewriteResponseCase } from './rewrite_request_case';
const paramSchema = schema.object({
id: schema.string(),
@ -21,40 +23,43 @@ const bodySchema = schema.object({
secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
});
const rewriteBodyRes: RewriteResponseCase<ActionResult> = ({
actionTypeId,
isPreconfigured,
...res
}) => ({
...res,
connector_type_id: actionTypeId,
is_preconfigured: isPreconfigured,
});
export const updateActionRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.put(
{
path: `${BASE_ACTION_API_PATH}/action/{id}`,
path: `${BASE_ACTION_API_PATH}/connector/{id}`,
validate: {
body: bodySchema,
params: paramSchema,
},
},
router.handleLegacyErrors(async function (context, req, res) {
verifyApiAccess(licenseState);
if (!context.actions) {
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
const { name, config, secrets } = req.body;
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const actionsClient = context.actions.getActionsClient();
const { id } = req.params;
const { name, config, secrets } = req.body;
try {
return res.ok({
body: await actionsClient.update({
id,
action: { name, config, secrets },
}),
body: rewriteBodyRes(
await actionsClient.update({
id,
action: { name, config, secrets },
})
),
});
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(res);
}
throw e;
}
})
})
)
);
};

View file

@ -0,0 +1,69 @@
/*
* 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 { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess, ActionTypeDisabledError } from '../lib';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { actionsClientMock } from '../actions_client.mock';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('../lib/verify_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
});
describe('verifyAccessAndContext', () => {
it('ensures the license allows creating actions', async () => {
const licenseState = licenseStateMock.create();
const handler = jest.fn();
const verify = verifyAccessAndContext(licenseState, handler);
const actionsClient = actionsClientMock.create();
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
await verify(context, req, res);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('ensures the license check prevents creating actions', async () => {
const licenseState = licenseStateMock.create();
(verifyApiAccess as jest.Mock).mockImplementation(() => {
throw new Error('OMG');
});
const handler = jest.fn();
const verify = verifyAccessAndContext(licenseState, handler);
const actionsClient = actionsClientMock.create();
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
await expect(verify(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
});
it('supports error that handle their own response', async () => {
const licenseState = licenseStateMock.create();
const handler = jest.fn();
const verify = verifyAccessAndContext(licenseState, handler);
const actionsClient = actionsClientMock.create();
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok', 'forbidden']);
handler.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
await expect(verify(context, req, res)).resolves.toMatchObject({ body: { message: 'Fail' } });
expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
});
});

View file

@ -0,0 +1,34 @@
/*
* 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 { RequestHandler } from 'kibana/server';
import { ILicenseState, isErrorThatHandlesItsOwnResponse, verifyApiAccess } from '../lib';
import { ActionsRequestHandlerContext } from '../types';
type ActionsRequestHandlerWrapper = <P, Q, B>(
licenseState: ILicenseState,
handler: RequestHandler<P, Q, B, ActionsRequestHandlerContext>
) => RequestHandler<P, Q, B, ActionsRequestHandlerContext>;
export const verifyAccessAndContext: ActionsRequestHandlerWrapper = (licenseState, handler) => {
return async (context, request, response) => {
verifyApiAccess(licenseState);
if (!context.actions) {
return response.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
}
try {
return await handler(context, request, response);
} catch (e) {
if (isErrorThatHandlesItsOwnResponse(e)) {
return e.sendResponse(response);
}
throw e;
}
};
};

View file

@ -18,11 +18,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should return 200 when creating an email action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
service: '__json',
from: 'bob@example.com',
@ -38,9 +38,9 @@ export default function emailTest({ getService }: FtrProviderContext) {
createdActionId = createdAction.id;
expect(createdAction).to.eql({
id: createdActionId,
isPreconfigured: false,
is_preconfigured: false,
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
service: '__json',
hasAuth: true,
@ -54,14 +54,14 @@ export default function emailTest({ getService }: FtrProviderContext) {
expect(typeof createdActionId).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdActionId}`)
.get(`/api/actions/connector/${createdActionId}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
from: 'bob@example.com',
service: '__json',
@ -75,7 +75,7 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should return the message data when firing the __json service', async () => {
await supertest
.post(`/api/actions/action/${createdActionId}/_execute`)
.post(`/api/actions/connector/${createdActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -119,7 +119,7 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should render html from markdown', async () => {
await supertest
.post(`/api/actions/action/${createdActionId}/_execute`)
.post(`/api/actions/connector/${createdActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -142,7 +142,7 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should allow customizing the kibana footer link', async () => {
await supertest
.post(`/api/actions/action/${createdActionId}/_execute`)
.post(`/api/actions/connector/${createdActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -169,11 +169,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating an email action with an invalid config', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {},
})
.expect(400)
@ -189,11 +189,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating an email action with a server not added to allowedHosts', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
service: 'gmail', // not added to allowedHosts in the config for this test
from: 'bob@example.com',
@ -214,11 +214,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
});
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
host: 'stmp.gmail.com', // not added to allowedHosts in the config for this test
port: 666,
@ -242,11 +242,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should handle creating an email action with a server added to allowedHosts', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
host: 'some.non.existent.com', // added to allowedHosts in the config for this test
port: 666,
@ -263,11 +263,11 @@ export default function emailTest({ getService }: FtrProviderContext) {
it('should handle an email action with no auth', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An email action with no auth',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
service: '__json',
from: 'jim@example.com',
@ -276,7 +276,7 @@ export default function emailTest({ getService }: FtrProviderContext) {
.expect(200);
await supertest
.post(`/api/actions/action/${createdAction.id}/_execute`)
.post(`/api/actions/connector/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -26,11 +26,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should be created successfully', async () => {
// create action with no config
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
},
@ -40,9 +40,9 @@ export default function indexTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: false,
@ -53,24 +53,24 @@ export default function indexTest({ getService }: FtrProviderContext) {
expect(typeof createdActionID).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdActionID}`)
.get(`/api/actions/connector/${createdActionID}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null },
});
// create action with all config props
const { body: createdActionWithIndex } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action with index config',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -81,9 +81,9 @@ export default function indexTest({ getService }: FtrProviderContext) {
expect(createdActionWithIndex).to.eql({
id: createdActionWithIndex.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An index action with index config',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -94,14 +94,14 @@ export default function indexTest({ getService }: FtrProviderContext) {
expect(typeof createdActionIDWithIndex).to.be('string');
const { body: fetchedActionWithIndex } = await supertest
.get(`/api/actions/action/${createdActionIDWithIndex}`)
.get(`/api/actions/connector/${createdActionIDWithIndex}`)
.expect(200);
expect(fetchedActionWithIndex).to.eql({
id: fetchedActionWithIndex.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An index action with index config',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -112,11 +112,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should respond with error when creation unsuccessful', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: { index: 666 },
})
.expect(400)
@ -132,11 +132,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should execute successly when expected for a single body', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -145,7 +145,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
})
.expect(200);
const { body: result } = await supertest
.post(`/api/actions/action/${createdAction.id}/_execute`)
.post(`/api/actions/connector/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -162,11 +162,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should execute successly when expected for with multiple bodies', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -175,7 +175,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
})
.expect(200);
const { body: result } = await supertest
.post(`/api/actions/action/${createdAction.id}/_execute`)
.post(`/api/actions/connector/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -206,11 +206,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should execute successly with refresh false', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: false,
@ -220,7 +220,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
})
.expect(200);
const { body: result } = await supertest
.post(`/api/actions/action/${createdAction.id}/_execute`)
.post(`/api/actions/connector/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -235,11 +235,11 @@ export default function indexTest({ getService }: FtrProviderContext) {
expect(items.length).to.be.lessThan(2);
const { body: createdActionWithRefresh } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An index action',
actionTypeId: '.index',
connector_type_id: '.index',
config: {
index: ES_TEST_INDEX_NAME,
refresh: true,
@ -248,7 +248,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
})
.expect(200);
const { body: result2 } = await supertest
.post(`/api/actions/action/${createdActionWithRefresh.id}/_execute`)
.post(`/api/actions/connector/${createdActionWithRefresh.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -24,7 +24,7 @@ export default function indexTest({ getService }: FtrProviderContext) {
it('should execute successfully when expected for a single body', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${ACTION_ID}/_execute`)
.post(`/api/actions/connector/${ACTION_ID}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -60,11 +60,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
describe('Jira - Action Creation', () => {
it('should return 200 when creating a jira action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
...mockJira.config,
apiUrl: jiraSimulatorURL,
@ -75,9 +75,9 @@ export default function jiraTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
apiUrl: jiraSimulatorURL,
projectKey: mockJira.config.projectKey,
@ -85,14 +85,14 @@ export default function jiraTest({ getService }: FtrProviderContext) {
});
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
apiUrl: jiraSimulatorURL,
projectKey: mockJira.config.projectKey,
@ -102,11 +102,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a jira action with no apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: { projectKey: 'CK' },
})
.expect(400)
@ -122,11 +122,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a jira action with no projectKey', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: { apiUrl: jiraSimulatorURL },
})
.expect(400)
@ -142,11 +142,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a jira action with a not present in allowedHosts apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
apiUrl: 'http://jira.mynonexistent.com',
projectKey: mockJira.config.projectKey,
@ -166,11 +166,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a jira action without secrets', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira action',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
apiUrl: jiraSimulatorURL,
projectKey: mockJira.config.projectKey,
@ -195,11 +195,11 @@ export default function jiraTest({ getService }: FtrProviderContext) {
before(async () => {
const { body } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A jira simulator',
actionTypeId: '.jira',
connector_type_id: '.jira',
config: {
apiUrl: jiraSimulatorURL,
projectKey: mockJira.config.projectKey,
@ -220,14 +220,14 @@ export default function jiraTest({ getService }: FtrProviderContext) {
describe('Validation', () => {
it('should handle failing with a simulated success without action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {},
})
.then((resp: any) => {
expect(Object.keys(resp.body)).to.eql(['status', 'actionId', 'message', 'retry']);
expect(resp.body.actionId).to.eql(simulatedActionId);
expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']);
expect(resp.body.connector_id).to.eql(simulatedActionId);
expect(resp.body.status).to.eql('error');
expect(resp.body.retry).to.eql(false);
// Node.js 12 oddity:
@ -261,14 +261,14 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without unsupported action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'non-supported' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -279,14 +279,14 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without subActionParams', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'pushToService' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -297,7 +297,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without title', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -312,7 +312,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -323,7 +323,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without commentId', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -340,7 +340,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -351,7 +351,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without comment message', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -367,7 +367,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -378,7 +378,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success when labels containing a space', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -395,7 +395,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -408,7 +408,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
describe('Execution', () => {
it('should handle creating an incident without comments', async () => {
const { body } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -427,7 +427,7 @@ export default function jiraTest({ getService }: FtrProviderContext) {
expect(proxyHaveBeenCalled).to.equal(true);
expect(body).to.eql({
status: 'ok',
actionId: simulatedActionId,
connector_id: simulatedActionId,
data: {
id: '123',
title: 'CK-1',

View file

@ -45,11 +45,11 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should return successfully when passed valid create parameters', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A pagerduty action',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
config: {
apiUrl: pagerdutySimulatorURL,
},
@ -61,9 +61,9 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A pagerduty action',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
config: {
apiUrl: pagerdutySimulatorURL,
},
@ -72,14 +72,14 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
expect(typeof createdAction.id).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A pagerduty action',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
config: {
apiUrl: pagerdutySimulatorURL,
},
@ -88,11 +88,11 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should return unsuccessfully when passed invalid create parameters', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A pagerduty action',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
config: {
apiUrl: pagerdutySimulatorURL,
},
@ -111,11 +111,11 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should return unsuccessfully when default pagerduty url is not present in allowedHosts', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A pagerduty action',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
secrets: {},
})
.expect(400)
@ -131,11 +131,11 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should create pagerduty simulator action successfully', async () => {
const { body: createdSimulatedAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A pagerduty simulator',
actionTypeId: '.pagerduty',
connector_type_id: '.pagerduty',
config: {
apiUrl: pagerdutySimulatorURL,
},
@ -150,7 +150,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should handle executing with a simulated success', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -162,7 +162,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
expect(proxyHaveBeenCalled).to.equal(true);
expect(result).to.eql({
status: 'ok',
actionId: simulatedActionId,
connector_id: simulatedActionId,
data: {
message: 'Event processed',
status: 'success',
@ -172,7 +172,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should handle a 40x pagerduty error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -186,7 +186,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should handle a 429 pagerduty error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -204,7 +204,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) {
it('should handle a 500 pagerduty error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -63,11 +63,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
describe('IBM Resilient - Action Creation', () => {
it('should return 200 when creating a ibm resilient action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An IBM Resilient action',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
...mockResilient.config,
apiUrl: resilientSimulatorURL,
@ -78,9 +78,9 @@ export default function resilientTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An IBM Resilient action',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
apiUrl: resilientSimulatorURL,
orgId: mockResilient.config.orgId,
@ -88,14 +88,14 @@ export default function resilientTest({ getService }: FtrProviderContext) {
});
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'An IBM Resilient action',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
apiUrl: resilientSimulatorURL,
orgId: mockResilient.config.orgId,
@ -105,11 +105,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a ibm resilient action with no apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An IBM Resilient',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: { orgId: '201' },
})
.expect(400)
@ -125,11 +125,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a ibm resilient action with no orgId', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An IBM Resilient',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: { apiUrl: resilientSimulatorURL },
})
.expect(400)
@ -145,11 +145,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a ibm resilient action with a not present in allowedHosts apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An IBM Resilient',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
apiUrl: 'http://resilient.mynonexistent.com',
orgId: mockResilient.config.orgId,
@ -169,11 +169,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a ibm resilient action without secrets', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'An IBM Resilient',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
apiUrl: resilientSimulatorURL,
orgId: mockResilient.config.orgId,
@ -197,11 +197,11 @@ export default function resilientTest({ getService }: FtrProviderContext) {
let proxyHaveBeenCalled = false;
before(async () => {
const { body } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A ibm resilient simulator',
actionTypeId: '.resilient',
connector_type_id: '.resilient',
config: {
apiUrl: resilientSimulatorURL,
orgId: mockResilient.config.orgId,
@ -222,14 +222,14 @@ export default function resilientTest({ getService }: FtrProviderContext) {
describe('Validation', () => {
it('should handle failing with a simulated success without action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {},
})
.then((resp: any) => {
expect(Object.keys(resp.body)).to.eql(['status', 'actionId', 'message', 'retry']);
expect(resp.body.actionId).to.eql(simulatedActionId);
expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']);
expect(resp.body.connector_id).to.eql(simulatedActionId);
expect(resp.body.status).to.eql('error');
expect(resp.body.retry).to.eql(false);
// Node.js 12 oddity:
@ -263,14 +263,14 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without unsupported action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'non-supported' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -281,14 +281,14 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without subActionParams', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'pushToService' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -299,7 +299,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without title', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -314,7 +314,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -325,7 +325,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without commentId', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -341,7 +341,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -352,7 +352,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without comment message', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -368,7 +368,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -381,7 +381,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
describe('Execution', () => {
it('should handle creating an incident without comments', async () => {
const { body } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -397,7 +397,7 @@ export default function resilientTest({ getService }: FtrProviderContext) {
expect(proxyHaveBeenCalled).to.equal(true);
expect(body).to.eql({
status: 'ok',
actionId: simulatedActionId,
connector_id: simulatedActionId,
data: {
id: '123',
title: '123',

View file

@ -18,41 +18,41 @@ export default function serverLogTest({ getService }: FtrProviderContext) {
it('should return 200 when creating a builtin server-log action', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A server.log action',
actionTypeId: '.server-log',
connector_type_id: '.server-log',
})
.expect(200);
serverLogActionId = createdAction.id;
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A server.log action',
actionTypeId: '.server-log',
connector_type_id: '.server-log',
config: {},
});
expect(typeof createdAction.id).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A server.log action',
actionTypeId: '.server-log',
connector_type_id: '.server-log',
config: {},
});
});
it('should handle firing the action', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${serverLogActionId}/_execute`)
.post(`/api/actions/connector/${serverLogActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -65,11 +65,11 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
describe('ServiceNow - Action Creation', () => {
it('should return 200 when creating a servicenow action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: servicenowSimulatorURL,
},
@ -79,23 +79,23 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: servicenowSimulatorURL,
},
});
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: servicenowSimulatorURL,
},
@ -104,11 +104,11 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {},
})
.expect(400)
@ -124,11 +124,11 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a servicenow action with a not present in allowedHosts apiUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: 'http://servicenow.mynonexistent.com',
},
@ -147,11 +147,11 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A servicenow action',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: servicenowSimulatorURL,
},
@ -174,11 +174,11 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
let proxyHaveBeenCalled = false;
before(async () => {
const { body } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A servicenow simulator',
actionTypeId: '.servicenow',
connector_type_id: '.servicenow',
config: {
apiUrl: servicenowSimulatorURL,
},
@ -198,14 +198,14 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
describe('Validation', () => {
it('should handle failing with a simulated success without action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {},
})
.then((resp: any) => {
expect(Object.keys(resp.body)).to.eql(['status', 'actionId', 'message', 'retry']);
expect(resp.body.actionId).to.eql(simulatedActionId);
expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']);
expect(resp.body.connector_id).to.eql(simulatedActionId);
expect(resp.body.status).to.eql('error');
expect(resp.body.retry).to.eql(false);
// Node.js 12 oddity:
@ -239,14 +239,14 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without unsupported action', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'non-supported' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -257,14 +257,14 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without subActionParams', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: { subAction: 'pushToService' },
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -275,7 +275,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without title', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -287,7 +287,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -298,7 +298,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without commentId', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -314,7 +314,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -325,7 +325,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
it('should handle failing with a simulated success without comment message', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -341,7 +341,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -353,7 +353,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
describe('getChoices', () => {
it('should fail when field is not provided', async () => {
await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -363,7 +363,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
})
.then((resp: any) => {
expect(resp.body).to.eql({
actionId: simulatedActionId,
connector_id: simulatedActionId,
status: 'error',
retry: false,
message:
@ -377,7 +377,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
describe('Execution', () => {
it('should handle creating an incident without comments', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -393,7 +393,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
expect(proxyHaveBeenCalled).to.equal(true);
expect(result).to.eql({
status: 'ok',
actionId: simulatedActionId,
connector_id: simulatedActionId,
data: {
id: '123',
title: 'INC01',
@ -406,7 +406,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
describe('getChoices', () => {
it('should get choices', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -419,7 +419,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
expect(proxyHaveBeenCalled).to.equal(true);
expect(result).to.eql({
status: 'ok',
actionId: simulatedActionId,
connector_id: simulatedActionId,
data: [
{
dependent_value: '',

View file

@ -45,11 +45,11 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should return 200 when creating a slack action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
secrets: {
webhookUrl: slackSimulatorURL,
},
@ -58,34 +58,34 @@ export default function slackTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
config: {},
});
expect(typeof createdAction.id).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
config: {},
});
});
it('should respond with a 400 Bad Request when creating a slack action with no webhookUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
secrets: {},
})
.expect(400)
@ -101,11 +101,11 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a slack action with not present in allowedHosts webhookUrl', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
secrets: {
webhookUrl: 'http://slack.mynonexistent.com/other/stuff/in/the/path',
},
@ -122,11 +122,11 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should respond with a 400 Bad Request when creating a slack action with a webhookUrl with no hostname', async () => {
await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A slack action',
actionTypeId: '.slack',
connector_type_id: '.slack',
secrets: {
webhookUrl: 'fee-fi-fo-fum',
},
@ -144,11 +144,11 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should create our slack simulator action successfully', async () => {
const { body: createdSimulatedAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'foo')
.send({
name: 'A slack simulator',
actionTypeId: '.slack',
connector_type_id: '.slack',
secrets: {
webhookUrl: slackSimulatorURL,
},
@ -160,7 +160,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should handle firing with a simulated success', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -174,7 +174,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should handle an empty message error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -188,7 +188,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should handle a 40x slack error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -203,7 +203,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should handle a 429 slack error', async () => {
const dateStart = new Date().getTime();
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -221,7 +221,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
it('should handle a 500 slack error', async () => {
const { body: result } = await supertest
.post(`/api/actions/action/${simulatedActionId}/_execute`)
.post(`/api/actions/connector/${simulatedActionId}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {

View file

@ -54,11 +54,11 @@ export default function webhookTest({ getService }: FtrProviderContext) {
};
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'test')
.send({
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
secrets: {
user,
password,
@ -99,11 +99,11 @@ export default function webhookTest({ getService }: FtrProviderContext) {
it('should return 200 when creating a webhook action successfully', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'test')
.send({
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
secrets: {
user: 'username',
password: 'mypassphrase',
@ -116,9 +116,9 @@ export default function webhookTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
config: {
...defaultValues,
url: webhookSimulatorURL,
@ -128,14 +128,14 @@ export default function webhookTest({ getService }: FtrProviderContext) {
expect(typeof createdAction.id).to.be('string');
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
config: {
...defaultValues,
url: webhookSimulatorURL,
@ -145,11 +145,11 @@ export default function webhookTest({ getService }: FtrProviderContext) {
it('should remove headers when a webhook is updated', async () => {
const { body: createdAction } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'test')
.send({
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
secrets: {
user: 'username',
password: 'mypassphrase',
@ -165,9 +165,9 @@ export default function webhookTest({ getService }: FtrProviderContext) {
expect(createdAction).to.eql({
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
config: {
...defaultValues,
url: webhookSimulatorURL,
@ -178,7 +178,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
});
await supertest
.put(`/api/actions/action/${createdAction.id}`)
.put(`/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'A generic Webhook action',
@ -196,14 +196,14 @@ export default function webhookTest({ getService }: FtrProviderContext) {
.expect(200);
const { body: fetchedAction } = await supertest
.get(`/api/actions/action/${createdAction.id}`)
.get(`/api/actions/connector/${createdAction.id}`)
.expect(200);
expect(fetchedAction).to.eql({
id: fetchedAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
config: {
...defaultValues,
url: webhookSimulatorURL,
@ -217,7 +217,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
it('should send authentication to the webhook target', async () => {
const webhookActionId = await createWebhookAction(webhookSimulatorURL, {}, kibanaURL);
const { body: result } = await supertest
.post(`/api/actions/action/${webhookActionId}/_execute`)
.post(`/api/actions/connector/${webhookActionId}/_execute`)
.set('kbn-xsrf', 'test')
.send({
params: {
@ -236,7 +236,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
kibanaURL
);
const { body: result } = await supertest
.post(`/api/actions/action/${webhookActionId}/_execute`)
.post(`/api/actions/connector/${webhookActionId}/_execute`)
.set('kbn-xsrf', 'test')
.send({
params: {
@ -255,7 +255,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
kibanaURL
);
const { body: result } = await supertest
.post(`/api/actions/action/${webhookActionId}/_execute`)
.post(`/api/actions/connector/${webhookActionId}/_execute`)
.set('kbn-xsrf', 'test')
.send({
params: {
@ -270,11 +270,11 @@ export default function webhookTest({ getService }: FtrProviderContext) {
it('should handle target webhooks that are not added to allowedHosts', async () => {
const { body: result } = await supertest
.post('/api/actions/action')
.post('/api/actions/connector')
.set('kbn-xsrf', 'test')
.send({
name: 'A generic Webhook action',
actionTypeId: '.webhook',
connector_type_id: '.webhook',
secrets: {
user: 'username',
password: 'mypassphrase',
@ -296,7 +296,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
kibanaURL
);
const { body: result } = await supertest
.post(`/api/actions/action/${webhookActionId}/_execute`)
.post(`/api/actions/connector/${webhookActionId}/_execute`)
.set('kbn-xsrf', 'test')
.send({
params: {
@ -312,7 +312,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
it('should handle failing webhook targets', async () => {
const webhookActionId = await createWebhookAction(webhookSimulatorURL, {}, kibanaURL);
const { body: result } = await supertest
.post(`/api/actions/action/${webhookActionId}/_execute`)
.post(`/api/actions/connector/${webhookActionId}/_execute`)
.set('kbn-xsrf', 'test')
.send({
params: {
@ -323,7 +323,7 @@ export default function webhookTest({ getService }: FtrProviderContext) {
expect(result.status).to.eql('error');
expect(result.message).to.match(/error calling webhook, retry later/);
expect(result.serviceMessage).to.eql('[500] Internal Server Error');
expect(result.service_message).to.eql('[500] Internal Server Error');
});
after(() => {

View file

@ -20,7 +20,7 @@ export default function listActionTypesTests({ getService }: FtrProviderContext)
describe(scenario.id, () => {
it('should return 200 with list of action types containing defaults', async () => {
const response = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/actions/list_action_types`)
.get(`${getUrlPrefix(space.id)}/api/actions/connector_types`)
.auth(user.username, user.password);
function createActionTypeMatcher(id: string, name: string) {

View file

@ -25,12 +25,12 @@ export default function createActionTests({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle create action request appropriately', async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -58,9 +58,9 @@ export default function createActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, response.body.id, 'action', 'actions');
expect(response.body).to.eql({
id: response.body.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -81,12 +81,12 @@ export default function createActionTests({ getService }: FtrProviderContext) {
it(`should handle create action request appropriately when action type isn't registered`, async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
name: 'My action',
actionTypeId: 'test.unregistered-action-type',
connector_type_id: 'test.unregistered-action-type',
config: {},
});
@ -119,7 +119,7 @@ export default function createActionTests({ getService }: FtrProviderContext) {
it('should handle create action request appropriately when payload is empty and invalid', async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({});
@ -146,12 +146,12 @@ export default function createActionTests({ getService }: FtrProviderContext) {
it(`should handle create action request appropriately when config isn't valid`, async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
name: 'my name',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: 'my unencrypted text',
},
@ -187,12 +187,12 @@ export default function createActionTests({ getService }: FtrProviderContext) {
it(`should handle create action requests for action types that are not enabled`, async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
name: 'my name',
actionTypeId: 'test.not-enabled',
connector_type_id: 'test.not-enabled',
});
switch (scenario.id) {

View file

@ -26,11 +26,11 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle delete action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -41,7 +41,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
.expect(200);
const response = await supertestWithoutAuth
.delete(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.delete(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo');
@ -71,11 +71,11 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it(`shouldn't delete action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -87,7 +87,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.delete(`${getUrlPrefix('other')}/api/actions/action/${createdAction.id}`)
.delete(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo');
@ -120,7 +120,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it(`should handle delete request appropriately when action doesn't exist`, async () => {
const response = await supertestWithoutAuth
.delete(`${getUrlPrefix(space.id)}/api/actions/action/2`)
.delete(`${getUrlPrefix(space.id)}/api/actions/connector/2`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password);
@ -148,7 +148,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it(`shouldn't delete action from preconfigured list`, async () => {
const response = await supertestWithoutAuth
.delete(`${getUrlPrefix(space.id)}/api/actions/action/my-slack1`)
.delete(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo');

View file

@ -48,11 +48,11 @@ export default function ({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle execute request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -65,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) {
const reference = `actions-execute-1:${user.username}`;
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -117,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) {
await validateEventLog({
spaceId: space.id,
actionId: createdAction.id,
connectorId: createdAction.id,
outcome: 'success',
message: `action executed: test.index-record:${createdAction.id}: My action`,
});
@ -129,11 +129,11 @@ export default function ({ getService }: FtrProviderContext) {
it(`shouldn't execute an action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -146,7 +146,7 @@ export default function ({ getService }: FtrProviderContext) {
const reference = `actions-execute-4:${user.username}`;
const response = await supertestWithoutAuth
.post(`${getUrlPrefix('other')}/api/actions/action/${createdAction.id}/_execute`)
.post(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -186,11 +186,11 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle execute request appropriately after action is updated', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -202,7 +202,7 @@ export default function ({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
await supertest
.put(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
@ -217,7 +217,7 @@ export default function ({ getService }: FtrProviderContext) {
const reference = `actions-execute-2:${user.username}`;
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -274,7 +274,7 @@ export default function ({ getService }: FtrProviderContext) {
it(`should handle execute request appropriately when action doesn't exist`, async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/1/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/1/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -310,7 +310,7 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle execute request appropriately when payload is empty and invalid', async () => {
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/1/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/1/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({});
@ -338,11 +338,11 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle execute request appropriately after changing config properties', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'test email action',
actionTypeId: '.email',
connector_type_id: '.email',
config: {
from: 'email-from-1@example.com',
// this host is specifically added to allowedHosts in:
@ -359,7 +359,7 @@ export default function ({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
await supertest
.put(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'a test email action 2',
@ -375,7 +375,7 @@ export default function ({ getService }: FtrProviderContext) {
.expect(200);
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -413,17 +413,17 @@ export default function ({ getService }: FtrProviderContext) {
let searchResult: any;
const reference = `actions-execute-3:${user.username}`;
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.authorization',
connector_type_id: 'test.authorization',
})
.expect(200);
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.post(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}/_execute`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -503,21 +503,21 @@ export default function ({ getService }: FtrProviderContext) {
interface ValidateEventLogParams {
spaceId: string;
actionId: string;
connectorId: string;
outcome: string;
message: string;
errorMessage?: string;
}
async function validateEventLog(params: ValidateEventLogParams): Promise<void> {
const { spaceId, actionId, outcome, message, errorMessage } = params;
const { spaceId, connectorId, outcome, message, errorMessage } = params;
const events: IValidatedEvent[] = await retry.try(async () => {
return await getEventLog({
getService,
spaceId,
type: 'action',
id: actionId,
id: connectorId,
provider: 'actions',
actions: new Map([['execute', { equal: 1 }]]),
filter: 'event.action:(execute)',
@ -550,7 +550,7 @@ export default function ({ getService }: FtrProviderContext) {
{
rel: 'primary',
type: 'action',
id: actionId,
id: connectorId,
namespace: spaceId,
},
]);

View file

@ -25,11 +25,11 @@ export default function getActionTests({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle get action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -41,7 +41,7 @@ export default function getActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.get(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -62,8 +62,8 @@ export default function getActionTests({ getService }: FtrProviderContext) {
expect(response.statusCode).to.eql(200);
expect(response.body).to.eql({
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
is_preconfigured: false,
connector_type_id: 'test.index-record',
name: 'My action',
config: {
unencrypted: `This value shouldn't get encrypted`,
@ -77,11 +77,11 @@ export default function getActionTests({ getService }: FtrProviderContext) {
it(`action shouldn't be acessible from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -93,7 +93,7 @@ export default function getActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.get(`${getUrlPrefix('other')}/api/actions/action/${createdAction.id}`)
.get(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -125,7 +125,7 @@ export default function getActionTests({ getService }: FtrProviderContext) {
it('should handle get preconfigured action request appropriately', async () => {
const response = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/actions/action/my-slack1`)
.get(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -146,9 +146,9 @@ export default function getActionTests({ getService }: FtrProviderContext) {
expect(response.statusCode).to.eql(200);
expect(response.body).to.eql({
id: 'my-slack1',
actionTypeId: '.slack',
connector_type_id: '.slack',
name: 'Slack#xyz',
isPreconfigured: true,
is_preconfigured: true,
});
break;
default:

View file

@ -25,11 +25,11 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle get all action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -41,7 +41,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/actions`)
.get(`${getUrlPrefix(space.id)}/api/actions/connectors`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -63,41 +63,41 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
expect(response.body).to.eql([
{
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
is_preconfigured: true,
connector_type_id: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
is_preconfigured: true,
connector_type_id: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
is_preconfigured: true,
connector_type_id: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
referenced_by_count: 0,
},
]);
break;
@ -106,13 +106,13 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
}
});
it('should handle get all request appropriately with proper referencedByCount', async () => {
it('should handle get all request appropriately with proper referenced_by_count', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -148,7 +148,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts');
const response = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/actions`)
.get(`${getUrlPrefix(space.id)}/api/actions/connectors`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -170,41 +170,41 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
expect(response.body).to.eql([
{
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
referencedByCount: 1,
referenced_by_count: 1,
},
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
is_preconfigured: true,
connector_type_id: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
referencedByCount: 1,
referenced_by_count: 1,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
is_preconfigured: true,
connector_type_id: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
is_preconfigured: true,
connector_type_id: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
referenced_by_count: 0,
},
]);
break;
@ -215,11 +215,11 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
it(`shouldn't get actions from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -231,7 +231,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.get(`${getUrlPrefix('other')}/api/actions`)
.get(`${getUrlPrefix('other')}/api/actions/connectors`)
.auth(user.username, user.password);
switch (scenario.id) {
@ -253,31 +253,31 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
expect(response.body).to.eql([
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
is_preconfigured: true,
connector_type_id: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
is_preconfigured: true,
connector_type_id: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
is_preconfigured: true,
connector_type_id: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
referenced_by_count: 0,
},
]);
break;

View file

@ -34,7 +34,7 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo
loadTestFile(require.resolve('./execute'));
loadTestFile(require.resolve('./get_all'));
loadTestFile(require.resolve('./get'));
loadTestFile(require.resolve('./list_action_types'));
loadTestFile(require.resolve('./connector_types'));
loadTestFile(require.resolve('./update'));
});
}

View file

@ -16,8 +16,8 @@ if (require.main === module) main();
async function main() {
let response;
response = await httpPost('api/actions/action', {
actionTypeId: '.email',
response = await httpPost('api/actions/connector', {
connector_type_id: '.email',
name: 'an email action',
config: {
from: 'patrick.mueller@elastic.co',
@ -32,12 +32,12 @@ async function main() {
});
console.log(`result of create: ${JSON.stringify(response, null, 4)}`);
const actionId = response.id;
const connectorId = response.id;
response = await httpGet(`api/actions/${actionId}`);
response = await httpGet(`api/actions/${connectorId}`);
console.log(`action after create: ${JSON.stringify(response, null, 4)}`);
response = await httpPut(`api/actions/action/${actionId}`, {
response = await httpPut(`api/actions/connector/${connectorId}`, {
name: 'an email action',
config: {
from: 'patrick.mueller@elastic.co',
@ -51,10 +51,10 @@ async function main() {
console.log(`response from update: ${JSON.stringify(response, null, 4)}`);
response = await httpGet(`api/actions/${actionId}`);
response = await httpGet(`api/actions/${connectorId}`);
console.log(`action after update: ${JSON.stringify(response, null, 4)}`);
response = await httpPost(`api/actions/action/${actionId}/_execute`, {
response = await httpPost(`api/actions/connector/${connectorId}/_execute`, {
params: {
to: ['patrick.mueller@elastic.co'],
subject: 'the email subject',

View file

@ -25,11 +25,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
describe(scenario.id, () => {
it('should handle update action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -41,7 +41,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -72,8 +72,8 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
expect(response.statusCode).to.eql(200);
expect(response.body).to.eql({
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
is_preconfigured: false,
connector_type_id: 'test.index-record',
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
@ -94,11 +94,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it(`shouldn't update action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -110,7 +110,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.put(`${getUrlPrefix('other')}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({
@ -152,7 +152,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it('should handle update action request appropriately when passing a null config', async () => {
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/1`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/1`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
@ -182,7 +182,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it(`should handle update action request appropriately when action doesn't exist`, async () => {
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/1`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/1`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
@ -224,7 +224,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it('should handle update action request appropriately when payload is empty and invalid', async () => {
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/1`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/1`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({});
@ -252,11 +252,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it('should handle update action request appropriately when secrets are not valid', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/action`)
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -268,7 +268,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
objectRemover.add(space.id, createdAction.id, 'action', 'actions');
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.auth(user.username, user.password)
.send({
@ -311,7 +311,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it(`shouldn't update action from preconfigured list`, async () => {
const response = await supertestWithoutAuth
.put(`${getUrlPrefix(space.id)}/api/actions/action/custom-system-abc-connector`)
.put(`${getUrlPrefix(space.id)}/api/actions/connector/custom-system-abc-connector`)
.auth(user.username, user.password)
.set('kbn-xsrf', 'foo')
.send({

View file

@ -14,10 +14,10 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context';
export default function listActionTypesTests({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
describe('list_action_types', () => {
it('should return 200 with list of action types containing defaults', async () => {
describe('connector_types', () => {
it('should return 200 with list of connector types containing defaults', async () => {
const response = await supertest.get(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/list_action_types`
`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector_types`
);
function createActionTypeMatcher(id: string, name: string) {
@ -33,5 +33,26 @@ export default function listActionTypesTests({ getService }: FtrProviderContext)
response.body.some(createActionTypeMatcher('test.index-record', 'Test: Index Record'))
).to.be(true);
});
describe('legacy', () => {
it('should return 200 with list of action types containing defaults', async () => {
const response = await supertest.get(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/list_action_types`
);
function createActionTypeMatcher(id: string, name: string) {
return (actionType: { id: string; name: string }) => {
return actionType.id === id && actionType.name === name;
};
}
expect(response.status).to.eql(200);
// Check for values explicitly in order to avoid this test failing each time plugins register
// a new action type
expect(
response.body.some(createActionTypeMatcher('test.index-record', 'Test: Index Record'))
).to.be(true);
});
});
});
}

View file

@ -19,13 +19,13 @@ export default function createActionTests({ getService }: FtrProviderContext) {
after(() => objectRemover.removeAll());
it('should handle create action request appropriately', async () => {
it('should handle create connector request appropriately', async () => {
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -38,9 +38,9 @@ export default function createActionTests({ getService }: FtrProviderContext) {
objectRemover.add(Spaces.space1.id, response.body.id, 'action', 'actions');
expect(response.body).to.eql({
id: response.body.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -56,6 +56,45 @@ export default function createActionTests({ getService }: FtrProviderContext) {
});
});
describe('legacy', () => {
it('should handle create action request appropriately', async () => {
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
});
expect(response.status).to.eql(200);
objectRemover.add(Spaces.space1.id, response.body.id, 'action', 'actions');
expect(response.body).to.eql({
id: response.body.id,
isPreconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
});
expect(typeof response.body.id).to.be('string');
// Ensure AAD isn't broken
await checkAAD({
supertest,
spaceId: Spaces.space1.id,
type: 'action',
id: response.body.id,
});
});
});
it('should notify feature usage when creating a gold action type', async () => {
const testStart = new Date();
const response = await supertest

View file

@ -20,11 +20,11 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it('should handle delete action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -35,18 +35,18 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
.expect(200);
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.expect(204, '');
});
it(`shouldn't delete action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -58,7 +58,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.delete(`${getUrlPrefix(Spaces.other.id)}/api/actions/action/${createdAction.id}`)
.delete(`${getUrlPrefix(Spaces.other.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.expect(404, {
statusCode: 404,
@ -69,7 +69,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it(`should handle delete request appropriately when action doesn't exist`, async () => {
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/2`)
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/2`)
.set('kbn-xsrf', 'foo')
.expect(404, {
statusCode: 404,
@ -80,7 +80,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
it(`shouldn't delete action from preconfigured list`, async () => {
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`)
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/my-slack1`)
.set('kbn-xsrf', 'foo')
.expect(400, {
statusCode: 400,
@ -88,5 +88,78 @@ export default function deleteActionTests({ getService }: FtrProviderContext) {
message: `Preconfigured action my-slack1 is not allowed to delete.`,
});
});
describe('legacy', () => {
it('should handle delete action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.expect(204, '');
});
it(`shouldn't delete action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.delete(`${getUrlPrefix(Spaces.other.id)}/api/actions/action/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.expect(404, {
statusCode: 404,
error: 'Not Found',
message: `Saved object [action/${createdAction.id}] not found`,
});
});
it(`should handle delete request appropriately when action doesn't exist`, async () => {
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/2`)
.set('kbn-xsrf', 'foo')
.expect(404, {
statusCode: 404,
error: 'Not Found',
message: 'Saved object [action/2] not found',
});
});
it(`shouldn't delete action from preconfigured list`, async () => {
await supertest
.delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`)
.set('kbn-xsrf', 'foo')
.expect(400, {
statusCode: 400,
error: 'Bad Request',
message: `Preconfigured action my-slack1 is not allowed to delete.`,
});
});
});
});
}

View file

@ -44,11 +44,11 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle execute request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -59,9 +59,11 @@ export default function ({ getService }: FtrProviderContext) {
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const reference = `actions-execute-1:${Spaces.space1.id}`;
const reference = `actions-execute-1:${Spaces.space1.id}:${createdAction.id}`;
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}/_execute`
)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -102,18 +104,20 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle failed executions', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'failing action',
actionTypeId: 'test.failing',
connector_type_id: 'test.failing',
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const reference = `actions-failure-1:${Spaces.space1.id}`;
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}/_execute`
)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -124,10 +128,10 @@ export default function ({ getService }: FtrProviderContext) {
expect(response.status).to.eql(200);
expect(response.body).to.eql({
actionId: createdAction.id,
connector_id: createdAction.id,
status: 'error',
message: 'an error occurred while running the action executor',
serviceMessage: `expected failure for ${ES_TEST_INDEX_NAME} ${reference}`,
service_message: `expected failure for ${ES_TEST_INDEX_NAME} ${reference}`,
retry: false,
});
@ -142,11 +146,11 @@ export default function ({ getService }: FtrProviderContext) {
it(`shouldn't execute an action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -178,17 +182,19 @@ export default function ({ getService }: FtrProviderContext) {
it('should handle execute request appropriately and have proper callCluster and savedObjectsClient authorization', async () => {
const reference = `actions-execute-3:${Spaces.space1.id}`;
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.authorization',
connector_type_id: 'test.authorization',
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}/_execute`
)
.set('kbn-xsrf', 'foo')
.send({
params: {
@ -220,11 +226,11 @@ export default function ({ getService }: FtrProviderContext) {
it('should notify feature usage when executing a gold action type', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'Noop action type',
actionTypeId: 'test.noop',
connector_type_id: 'test.noop',
secrets: {},
config: {},
})
@ -233,7 +239,9 @@ export default function ({ getService }: FtrProviderContext) {
const executionStart = new Date();
await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.post(
`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}/_execute`
)
.set('kbn-xsrf', 'foo')
.send({
params: {},
@ -251,6 +259,72 @@ export default function ({ getService }: FtrProviderContext) {
expect(noopFeature.last_used).to.be.a('string');
expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(executionStart.getTime());
});
describe('legacy', () => {
it('should handle execute request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const reference = `actions-execute-1:${Spaces.space1.id}:${createdAction.id}`;
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
reference,
index: ES_TEST_INDEX_NAME,
message: 'Testing 123',
},
});
expect(response.status).to.eql(200);
});
it('should handle failed executions', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'failing action',
actionTypeId: 'test.failing',
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const reference = `actions-failure-1:${Spaces.space1.id}:${createdAction.id}`;
const response = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`)
.set('kbn-xsrf', 'foo')
.send({
params: {
reference,
index: ES_TEST_INDEX_NAME,
},
});
expect(response.status).to.eql(200);
expect(response.body).to.eql({
actionId: createdAction.id,
status: 'error',
message: 'an error occurred while running the action executor',
serviceMessage: `expected failure for ${ES_TEST_INDEX_NAME} ${reference}`,
retry: false,
});
});
});
});
interface ValidateEventLogParams {

View file

@ -20,11 +20,11 @@ export default function getActionTests({ getService }: FtrProviderContext) {
it('should handle get action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -36,11 +36,11 @@ export default function getActionTests({ getService }: FtrProviderContext) {
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}`)
.expect(200, {
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
is_preconfigured: false,
connector_type_id: 'test.index-record',
name: 'My action',
config: {
unencrypted: `This value shouldn't get encrypted`,
@ -50,11 +50,11 @@ export default function getActionTests({ getService }: FtrProviderContext) {
it(`action should't be acessible from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -66,7 +66,7 @@ export default function getActionTests({ getService }: FtrProviderContext) {
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.get(`${getUrlPrefix(Spaces.other.id)}/api/actions/action/${createdAction.id}`)
.get(`${getUrlPrefix(Spaces.other.id)}/api/actions/connector/${createdAction.id}`)
.expect(404, {
statusCode: 404,
error: 'Not Found',
@ -76,13 +76,82 @@ export default function getActionTests({ getService }: FtrProviderContext) {
it('should handle get action request from preconfigured list', async () => {
await supertest
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`)
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/my-slack1`)
.expect(200, {
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
});
});
describe('legacy', () => {
it('should handle get action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.expect(200, {
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
name: 'My action',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
});
});
it(`action should't be acessible from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.get(`${getUrlPrefix(Spaces.other.id)}/api/actions/action/${createdAction.id}`)
.expect(404, {
statusCode: 404,
error: 'Not Found',
message: `Saved object [action/${createdAction.id}] not found`,
});
});
it('should handle get action request from preconfigured list', async () => {
await supertest
.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`)
.expect(200, {
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
name: 'Slack#xyz',
});
});
});
});
}

View file

@ -35,44 +35,44 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions`).expect(200, [
await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connectors`).expect(200, [
{
id: createdAction.id,
isPreconfigured: false,
is_preconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
is_preconfigured: true,
connector_type_id: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
is_preconfigured: true,
connector_type_id: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
is_preconfigured: true,
connector_type_id: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
referenced_by_count: 0,
},
]);
});
@ -94,36 +94,97 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest.get(`${getUrlPrefix(Spaces.other.id)}/api/actions`).expect(200, [
await supertest.get(`${getUrlPrefix(Spaces.other.id)}/api/actions/connectors`).expect(200, [
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
is_preconfigured: true,
connector_type_id: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
is_preconfigured: true,
connector_type_id: '.slack',
name: 'Slack#xyz',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
is_preconfigured: true,
connector_type_id: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
referenced_by_count: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
is_preconfigured: true,
connector_type_id: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
referenced_by_count: 0,
},
]);
});
describe('legacy', () => {
it('should handle get all action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/actions`).expect(200, [
{
id: createdAction.id,
isPreconfigured: false,
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
referencedByCount: 0,
},
{
id: 'preconfigured-es-index-action',
isPreconfigured: true,
actionTypeId: '.index',
name: 'preconfigured_es_index_action',
referencedByCount: 0,
},
{
id: 'my-slack1',
isPreconfigured: true,
actionTypeId: '.slack',
name: 'Slack#xyz',
referencedByCount: 0,
},
{
id: 'custom-system-abc-connector',
isPreconfigured: true,
actionTypeId: 'system-abc-action-type',
name: 'SystemABC',
referencedByCount: 0,
},
{
id: 'preconfigured.test.index-record',
isPreconfigured: true,
actionTypeId: 'test.index-record',
name: 'Test:_Preconfigured_Index_Record',
referencedByCount: 0,
},
]);
});
});
});
}

View file

@ -18,7 +18,7 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo
loadTestFile(require.resolve('./delete'));
loadTestFile(require.resolve('./get_all'));
loadTestFile(require.resolve('./get'));
loadTestFile(require.resolve('./list_action_types'));
loadTestFile(require.resolve('./connector_types'));
loadTestFile(require.resolve('./update'));
loadTestFile(require.resolve('./execute'));
loadTestFile(require.resolve('./builtin_action_types/es_index'));

View file

@ -21,11 +21,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it('should handle update action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -37,7 +37,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
@ -50,8 +50,8 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
})
.expect(200, {
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
is_preconfigured: false,
connector_type_id: 'test.index-record',
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
@ -69,11 +69,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it(`shouldn't update action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
connector_type_id: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
@ -105,7 +105,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it(`shouldn't update action from preconfigured list`, async () => {
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/custom-system-abc-connector`)
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/custom-system-abc-connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
@ -125,11 +125,11 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
it('should notify feature usage when editing a gold action type', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'Noop action type',
actionTypeId: 'test.noop',
connector_type_id: 'test.noop',
secrets: {},
config: {},
})
@ -138,7 +138,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
const updateStart = new Date();
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'Noop action type updated',
@ -158,5 +158,147 @@ export default function updateActionTests({ getService }: FtrProviderContext) {
expect(noopFeature.last_used).to.be.a('string');
expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(updateStart.getTime());
});
describe('legacy', () => {
it('should handle update action request appropriately', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200, {
id: createdAction.id,
isPreconfigured: false,
actionTypeId: 'test.index-record',
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
});
// Ensure AAD isn't broken
await checkAAD({
supertest,
spaceId: Spaces.space1.id,
type: 'action',
id: createdAction.id,
});
});
it(`shouldn't update action from another space`, async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action',
actionTypeId: 'test.index-record',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
await supertest
.put(`${getUrlPrefix(Spaces.other.id)}/api/actions/action/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(404, {
statusCode: 404,
error: 'Not Found',
message: `Saved object [action/${createdAction.id}] not found`,
});
});
it(`shouldn't update action from preconfigured list`, async () => {
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/custom-system-abc-connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'My action updated',
config: {
unencrypted: `This value shouldn't get encrypted`,
},
secrets: {
encrypted: 'This value should be encrypted',
},
})
.expect(400, {
statusCode: 400,
error: 'Bad Request',
message: `Preconfigured action custom-system-abc-connector is not allowed to update.`,
});
});
it('should notify feature usage when editing a gold action type', async () => {
const { body: createdAction } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send({
name: 'Noop action type',
actionTypeId: 'test.noop',
secrets: {},
config: {},
})
.expect(200);
objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions');
const updateStart = new Date();
await supertest
.put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`)
.set('kbn-xsrf', 'foo')
.send({
name: 'Noop action type updated',
secrets: {},
config: {},
})
.expect(200);
const {
body: { features },
} = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/licensing/feature_usage`);
expect(features).to.be.an(Array);
const noopFeature = features.find(
(feature: { name: string }) => feature.name === 'Connector: Test: Noop'
);
expect(noopFeature).to.be.ok();
expect(noopFeature.last_used).to.be.a('string');
expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(updateStart.getTime());
});
});
});
}