-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Alerting] Adds a builtin action for triggering webhooks (#43538)
Adds the ability to trigger webhooks using an action. This feature is currently locked off while we figure out the right privileges model.
- Loading branch information
Showing
12 changed files
with
745 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
x-pack/legacy/plugins/actions/server/builtin_action_types/lib/http_rersponse_retry_header.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { fromNullable, Option } from 'fp-ts/lib/Option'; | ||
|
||
export function getRetryAfterIntervalFromHeaders(headers: Record<string, string>): Option<number> { | ||
return fromNullable(headers['retry-after']) | ||
.map(retryAfter => parseInt(retryAfter, 10)) | ||
.filter(retryAfter => !isNaN(retryAfter)); | ||
} |
51 changes: 51 additions & 0 deletions
51
x-pack/legacy/plugins/actions/server/builtin_action_types/lib/result_type.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
// There appears to be an unexported implementation of Either in here: src/core/server/saved_objects/service/lib/repository.ts | ||
// Which is basically the Haskel equivalent of Rust/ML/Scala's Result | ||
// I'll reach out to other's in Kibana to see if we can merge these into one type | ||
|
||
// eslint-disable-next-line @typescript-eslint/prefer-interface | ||
export type Ok<T> = { | ||
tag: 'ok'; | ||
value: T; | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/prefer-interface | ||
export type Err<E> = { | ||
tag: 'err'; | ||
error: E; | ||
}; | ||
export type Result<T, E> = Ok<T> | Err<E>; | ||
|
||
export function asOk<T>(value: T): Ok<T> { | ||
return { | ||
tag: 'ok', | ||
value, | ||
}; | ||
} | ||
|
||
export function asErr<T>(error: T): Err<T> { | ||
return { | ||
tag: 'err', | ||
error, | ||
}; | ||
} | ||
|
||
export function isOk<T, E>(result: Result<T, E>): result is Ok<T> { | ||
return result.tag === 'ok'; | ||
} | ||
|
||
export function isErr<T, E>(result: Result<T, E>): result is Err<E> { | ||
return !isOk(result); | ||
} | ||
|
||
export async function promiseResult<T, E>(future: Promise<T>): Promise<Result<T, E>> { | ||
try { | ||
return asOk(await future); | ||
} catch (e) { | ||
return asErr(e); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
x-pack/legacy/plugins/actions/server/builtin_action_types/lib/schemas.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { schema } from '@kbn/config-schema'; | ||
|
||
const PORT_MAX = 256 * 256 - 1; | ||
export const portSchema = () => schema.number({ min: 1, max: PORT_MAX }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
x-pack/legacy/plugins/actions/server/builtin_action_types/webhook.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { actionType } from './webhook'; | ||
import { validateConfig, validateSecrets, validateParams } from '../lib'; | ||
|
||
describe('actionType', () => { | ||
test('exposes the action as `webhook` on its Id and Name', () => { | ||
expect(actionType.id).toEqual('.webhook'); | ||
expect(actionType.name).toEqual('webhook'); | ||
}); | ||
}); | ||
|
||
describe('secrets validation', () => { | ||
test('succeeds when secrets is valid', () => { | ||
const secrets: Record<string, any> = { | ||
user: 'bob', | ||
password: 'supersecret', | ||
}; | ||
expect(validateSecrets(actionType, secrets)).toEqual(secrets); | ||
}); | ||
|
||
test('fails when secret password is omitted', () => { | ||
expect(() => { | ||
validateSecrets(actionType, { user: 'bob' }); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type secrets: [password]: expected value of type [string] but got [undefined]"` | ||
); | ||
}); | ||
|
||
test('fails when secret user is omitted', () => { | ||
expect(() => { | ||
validateSecrets(actionType, {}); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type secrets: [user]: expected value of type [string] but got [undefined]"` | ||
); | ||
}); | ||
}); | ||
|
||
describe('config validation', () => { | ||
const defaultValues: Record<string, any> = { | ||
headers: null, | ||
method: 'post', | ||
}; | ||
|
||
test('config validation passes when only required fields are provided', () => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
}; | ||
expect(validateConfig(actionType, config)).toEqual({ | ||
...defaultValues, | ||
...config, | ||
}); | ||
}); | ||
|
||
test('config validation passes when valid methods are provided', () => { | ||
['post', 'put'].forEach(method => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
method, | ||
}; | ||
expect(validateConfig(actionType, config)).toEqual({ | ||
...defaultValues, | ||
...config, | ||
}); | ||
}); | ||
}); | ||
|
||
test('should validate and throw error when method on config is invalid', () => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
method: 'https', | ||
}; | ||
expect(() => { | ||
validateConfig(actionType, config); | ||
}).toThrowErrorMatchingInlineSnapshot(` | ||
"error validating action type config: [method]: types that failed validation: | ||
- [method.0]: expected value to equal [post] but got [https] | ||
- [method.1]: expected value to equal [put] but got [https]" | ||
`); | ||
}); | ||
|
||
test('config validation passes when a url is specified', () => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
}; | ||
expect(validateConfig(actionType, config)).toEqual({ | ||
...defaultValues, | ||
...config, | ||
}); | ||
}); | ||
|
||
test('config validation passes when valid headers are provided', () => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}; | ||
expect(validateConfig(actionType, config)).toEqual({ | ||
...defaultValues, | ||
...config, | ||
}); | ||
}); | ||
|
||
test('should validate and throw error when headers on config is invalid', () => { | ||
const config: Record<string, any> = { | ||
url: 'http://mylisteningserver:9200/endpoint', | ||
headers: 'application/json', | ||
}; | ||
expect(() => { | ||
validateConfig(actionType, config); | ||
}).toThrowErrorMatchingInlineSnapshot(` | ||
"error validating action type config: [headers]: types that failed validation: | ||
- [headers.0]: expected value of type [object] but got [string] | ||
- [headers.1]: expected value to equal [null] but got [application/json]" | ||
`); | ||
}); | ||
}); | ||
|
||
describe('params validation', () => { | ||
test('param validation passes when no fields are provided as none are required', () => { | ||
const params: Record<string, any> = {}; | ||
expect(validateParams(actionType, params)).toEqual({}); | ||
}); | ||
|
||
test('params validation passes when a valid body is provided', () => { | ||
const params: Record<string, any> = { | ||
body: 'count: {{ctx.payload.hits.total}}', | ||
}; | ||
expect(validateParams(actionType, params)).toEqual({ | ||
...params, | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.