mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[alerting] removes usage of any throughout Alerting Services code (#64161)
This removes unneeded use of `any` throughout: 1. alerting 2. alerting_builtin 3. actions 4. task manager 5. event log It also adds a linting rule that will prevent us from adding more `any` in the future unless an explicit exemption is made.
This commit is contained in:
parent
4051c94568
commit
a012ddf9df
131 changed files with 838 additions and 540 deletions
13
.eslintrc.js
13
.eslintrc.js
|
@ -739,6 +739,19 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Alerting Services overrides
|
||||
*/
|
||||
{
|
||||
// typescript only for front and back end
|
||||
files: [
|
||||
'x-pack/{,legacy/}plugins/{alerting,alerting_builtins,actions,task_manager,event_log}/**/*.{ts,tsx}',
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Lens overrides
|
||||
*/
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
import { Root } from 'joi';
|
||||
import { Legacy } from 'kibana';
|
||||
import mappings from './mappings.json';
|
||||
import {
|
||||
LegacyPluginApi,
|
||||
LegacyPluginSpec,
|
||||
ArrayOrItem,
|
||||
} from '../../../../../src/legacy/plugin_discovery/types';
|
||||
|
||||
export function actions(kibana: any) {
|
||||
export function actions(kibana: LegacyPluginApi): ArrayOrItem<LegacyPluginSpec> {
|
||||
return new kibana.Plugin({
|
||||
id: 'actions',
|
||||
configPrefix: 'xpack.actions',
|
||||
|
@ -29,5 +34,5 @@ export function actions(kibana: any) {
|
|||
uiExports: {
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
} as Legacy.PluginSpecOptions);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,13 @@
|
|||
import { Legacy } from 'kibana';
|
||||
import { Root } from 'joi';
|
||||
import mappings from './mappings.json';
|
||||
import {
|
||||
LegacyPluginApi,
|
||||
LegacyPluginSpec,
|
||||
ArrayOrItem,
|
||||
} from '../../../../../src/legacy/plugin_discovery/types';
|
||||
|
||||
export function alerting(kibana: any) {
|
||||
export function alerting(kibana: LegacyPluginApi): ArrayOrItem<LegacyPluginSpec> {
|
||||
return new kibana.Plugin({
|
||||
id: 'alerting',
|
||||
configPrefix: 'xpack.alerting',
|
||||
|
@ -31,5 +36,5 @@ export function alerting(kibana: any) {
|
|||
uiExports: {
|
||||
mappings,
|
||||
},
|
||||
});
|
||||
} as Legacy.PluginSpecOptions);
|
||||
}
|
||||
|
|
|
@ -15,19 +15,26 @@ export { LegacyTaskManagerApi, getTaskManagerSetup, getTaskManagerStart } from '
|
|||
// Once all plugins are migrated to NP, this can be removed
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { TaskManager } from '../../../../plugins/task_manager/server/task_manager';
|
||||
import {
|
||||
LegacyPluginApi,
|
||||
LegacyPluginSpec,
|
||||
ArrayOrItem,
|
||||
} from '../../../../../src/legacy/plugin_discovery/types';
|
||||
|
||||
const savedObjectSchemas = {
|
||||
task: {
|
||||
hidden: true,
|
||||
isNamespaceAgnostic: true,
|
||||
convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`,
|
||||
// legacy config is marked as any in core, no choice here
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
indexPattern(config: any) {
|
||||
return config.get('xpack.task_manager.index');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function taskManager(kibana: any) {
|
||||
export function taskManager(kibana: LegacyPluginApi): ArrayOrItem<LegacyPluginSpec> {
|
||||
return new kibana.Plugin({
|
||||
id: 'task_manager',
|
||||
require: ['kibana', 'elasticsearch', 'xpack_main'],
|
||||
|
@ -58,7 +65,11 @@ export function taskManager(kibana: any) {
|
|||
// instead we will start the internal Task Manager plugin when
|
||||
// all legacy plugins have finished initializing
|
||||
// Once all plugins are migrated to NP, this can be removed
|
||||
this.kbnServer.afterPluginsInit(() => {
|
||||
|
||||
// the typing for the lagcy server isn't quite correct, so
|
||||
// we'll bypase it for now
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this as any).kbnServer.afterPluginsInit(() => {
|
||||
taskManagerPlugin.start();
|
||||
});
|
||||
return taskManagerPlugin;
|
||||
|
@ -71,5 +82,5 @@ export function taskManager(kibana: any) {
|
|||
migrations,
|
||||
savedObjectSchemas,
|
||||
},
|
||||
});
|
||||
} as Legacy.PluginSpecOptions);
|
||||
}
|
||||
|
|
|
@ -49,10 +49,10 @@ export function createLegacyApi(legacyTaskManager: Promise<TaskManager>): Legacy
|
|||
fetch: (opts: SearchOpts) => legacyTaskManager.then((tm: TaskManager) => tm.fetch(opts)),
|
||||
get: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.get(id)),
|
||||
remove: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.remove(id)),
|
||||
schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: any) =>
|
||||
schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: object) =>
|
||||
legacyTaskManager.then((tm: TaskManager) => tm.schedule(taskInstance, options)),
|
||||
runNow: (taskId: string) => legacyTaskManager.then((tm: TaskManager) => tm.runNow(taskId)),
|
||||
ensureScheduled: (taskInstance: TaskInstanceWithId, options?: any) =>
|
||||
ensureScheduled: (taskInstance: TaskInstanceWithId, options?: object) =>
|
||||
legacyTaskManager.then((tm: TaskManager) => tm.ensureScheduled(taskInstance, options)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { SavedObject } from '../../../../../src/core/server';
|
|||
|
||||
export const migrations = {
|
||||
task: {
|
||||
'7.4.0': (doc: SavedObject<Record<any, any>>) => ({
|
||||
'7.4.0': (doc: SavedObject<Record<string, unknown>>) => ({
|
||||
...doc,
|
||||
updated_at: new Date().toISOString(),
|
||||
}),
|
||||
|
@ -18,7 +18,7 @@ export const migrations = {
|
|||
function moveIntervalIntoSchedule({
|
||||
attributes: { interval, ...attributes },
|
||||
...doc
|
||||
}: SavedObject<Record<any, any>>) {
|
||||
}: SavedObject<Record<string, unknown>>) {
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
|
|
|
@ -19,6 +19,8 @@ export interface ActionResult {
|
|||
id: string;
|
||||
actionTypeId: string;
|
||||
name: string;
|
||||
// This will have to remain `any` until we can extend Action Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
config: Record<string, any>;
|
||||
isPreconfigured: boolean;
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ export class ActionTypeRegistry {
|
|||
title: actionType.name,
|
||||
type: `actions:${actionType.id}`,
|
||||
maxAttempts: actionType.maxAttempts || 1,
|
||||
getRetry(attempts: number, error: any) {
|
||||
getRetry(attempts: number, error: unknown) {
|
||||
if (error instanceof ExecutorError) {
|
||||
return error.retry == null ? false : error.retry;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
import { ActionsClient } from './actions_client';
|
||||
|
||||
type ActionsClientContract = PublicMethodsOf<ActionsClient>;
|
||||
export type ActionsClientMock = jest.Mocked<ActionsClientContract>;
|
||||
|
||||
const createActionsClientMock = () => {
|
||||
const mocked: jest.Mocked<ActionsClientContract> = {
|
||||
const mocked: ActionsClientMock = {
|
||||
create: jest.fn(),
|
||||
get: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
|
@ -19,6 +20,8 @@ const createActionsClientMock = () => {
|
|||
return mocked;
|
||||
};
|
||||
|
||||
export const actionsClientMock = {
|
||||
export const actionsClientMock: {
|
||||
create: () => ActionsClientMock;
|
||||
} = {
|
||||
create: createActionsClientMock,
|
||||
};
|
||||
|
|
|
@ -135,7 +135,7 @@ export class ActionsClient {
|
|||
id,
|
||||
actionTypeId: result.attributes.actionTypeId as string,
|
||||
name: result.attributes.name as string,
|
||||
config: result.attributes.config as Record<string, any>,
|
||||
config: result.attributes.config as Record<string, unknown>,
|
||||
isPreconfigured: false,
|
||||
};
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ async function injectExtraFindData(
|
|||
scopedClusterClient: IScopedClusterClient,
|
||||
actionResults: ActionResult[]
|
||||
): Promise<FindActionResult[]> {
|
||||
const aggs: Record<string, any> = {};
|
||||
const aggs: Record<string, unknown> = {};
|
||||
for (const actionResult of actionResults) {
|
||||
aggs[actionResult.id] = {
|
||||
filter: {
|
||||
|
|
|
@ -30,7 +30,7 @@ const NO_OP_FN = () => {};
|
|||
|
||||
const services = {
|
||||
log: NO_OP_FN,
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ describe('actionTypeRegistry.get() works', () => {
|
|||
|
||||
describe('config validation', () => {
|
||||
test('config validation succeeds when config is valid', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, unknown> = {
|
||||
service: 'gmail',
|
||||
from: 'bob@example.com',
|
||||
};
|
||||
|
@ -74,7 +74,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('config validation fails when config is not valid', () => {
|
||||
const baseConfig: Record<string, any> = {
|
||||
const baseConfig: Record<string, unknown> = {
|
||||
from: 'bob@example.com',
|
||||
};
|
||||
|
||||
|
@ -177,7 +177,7 @@ describe('config validation', () => {
|
|||
|
||||
describe('secrets validation', () => {
|
||||
test('secrets validation succeeds when secrets is valid', () => {
|
||||
const secrets: Record<string, any> = {
|
||||
const secrets: Record<string, unknown> = {
|
||||
user: 'bob',
|
||||
password: 'supersecret',
|
||||
};
|
||||
|
@ -185,7 +185,7 @@ describe('secrets validation', () => {
|
|||
});
|
||||
|
||||
test('secrets validation succeeds when secrets props are null/undefined', () => {
|
||||
const secrets: Record<string, any> = {
|
||||
const secrets: Record<string, unknown> = {
|
||||
user: null,
|
||||
password: null,
|
||||
};
|
||||
|
@ -197,7 +197,7 @@ describe('secrets validation', () => {
|
|||
|
||||
describe('params validation', () => {
|
||||
test('params validation succeeds when params is valid', () => {
|
||||
const params: Record<string, any> = {
|
||||
const params: Record<string, unknown> = {
|
||||
to: ['bob@example.com'],
|
||||
subject: 'this is a test',
|
||||
message: 'this is the message',
|
||||
|
|
|
@ -30,10 +30,10 @@ const ConfigSchema = schema.object(ConfigSchemaProps);
|
|||
|
||||
function validateConfig(
|
||||
configurationUtilities: ActionsConfigurationUtilities,
|
||||
configObject: any
|
||||
configObject: unknown
|
||||
): string | void {
|
||||
// avoids circular reference ...
|
||||
const config: ActionTypeConfigType = configObject;
|
||||
const config = configObject as ActionTypeConfigType;
|
||||
|
||||
// Make sure service is set, or if not, both host/port must be set.
|
||||
// If service is set, host/port are ignored, when the email is sent.
|
||||
|
@ -95,9 +95,9 @@ const ParamsSchema = schema.object(
|
|||
}
|
||||
);
|
||||
|
||||
function validateParams(paramsObject: any): string | void {
|
||||
function validateParams(paramsObject: unknown): string | void {
|
||||
// avoids circular reference ...
|
||||
const params: ActionParamsType = paramsObject;
|
||||
const params = paramsObject as ActionParamsType;
|
||||
|
||||
const { to, cc, bcc } = params;
|
||||
const addrs = to.length + cc.length + bcc.length;
|
||||
|
|
|
@ -43,7 +43,7 @@ describe('actionTypeRegistry.get() works', () => {
|
|||
|
||||
describe('config validation', () => {
|
||||
test('config validation succeeds when config is valid', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, unknown> = {
|
||||
index: 'testing-123',
|
||||
refresh: false,
|
||||
};
|
||||
|
@ -97,7 +97,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('config validation fails when config is not valid', () => {
|
||||
const baseConfig: Record<string, any> = {
|
||||
const baseConfig: Record<string, unknown> = {
|
||||
indeX: 'bob',
|
||||
};
|
||||
|
||||
|
@ -111,7 +111,7 @@ describe('config validation', () => {
|
|||
|
||||
describe('params validation', () => {
|
||||
test('params validation succeeds when params is valid', () => {
|
||||
const params: Record<string, any> = {
|
||||
const params: Record<string, unknown> = {
|
||||
documents: [{ rando: 'thing' }],
|
||||
};
|
||||
expect(validateParams(actionType, params)).toMatchInlineSnapshot(`
|
||||
|
|
|
@ -72,13 +72,12 @@ async function executor(
|
|||
bulkBody.push(document);
|
||||
}
|
||||
|
||||
const bulkParams: any = {
|
||||
const bulkParams: unknown = {
|
||||
index,
|
||||
body: bulkBody,
|
||||
refresh: config.refresh,
|
||||
};
|
||||
|
||||
bulkParams.refresh = config.refresh;
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await services.callCluster('bulk', bulkParams);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Services } from '../../types';
|
|||
|
||||
interface PostPagerdutyOptions {
|
||||
apiUrl: string;
|
||||
data: any;
|
||||
data: unknown;
|
||||
headers: Record<string, string>;
|
||||
services: Services;
|
||||
}
|
||||
|
|
|
@ -63,12 +63,15 @@ describe('send_email module', () => {
|
|||
});
|
||||
|
||||
test('handles unauthenticated email using not secure host/port', async () => {
|
||||
const sendEmailOptions = getSendEmailOptions();
|
||||
const sendEmailOptions = getSendEmailOptions({
|
||||
transport: {
|
||||
host: 'example.com',
|
||||
port: 1025,
|
||||
},
|
||||
});
|
||||
delete sendEmailOptions.transport.service;
|
||||
delete sendEmailOptions.transport.user;
|
||||
delete sendEmailOptions.transport.password;
|
||||
sendEmailOptions.transport.host = 'example.com';
|
||||
sendEmailOptions.transport.port = 1025;
|
||||
const result = await sendEmail(mockLogger, sendEmailOptions);
|
||||
expect(result).toBe(sendMailMockResult);
|
||||
expect(createTransportMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
|
@ -105,13 +108,17 @@ describe('send_email module', () => {
|
|||
});
|
||||
|
||||
test('handles unauthenticated email using secure host/port', async () => {
|
||||
const sendEmailOptions = getSendEmailOptions();
|
||||
const sendEmailOptions = getSendEmailOptions({
|
||||
transport: {
|
||||
host: 'example.com',
|
||||
port: 1025,
|
||||
secure: true,
|
||||
},
|
||||
});
|
||||
delete sendEmailOptions.transport.service;
|
||||
delete sendEmailOptions.transport.user;
|
||||
delete sendEmailOptions.transport.password;
|
||||
sendEmailOptions.transport.host = 'example.com';
|
||||
sendEmailOptions.transport.port = 1025;
|
||||
sendEmailOptions.transport.secure = true;
|
||||
|
||||
const result = await sendEmail(mockLogger, sendEmailOptions);
|
||||
expect(result).toBe(sendMailMockResult);
|
||||
expect(createTransportMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
|
@ -154,19 +161,22 @@ describe('send_email module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
function getSendEmailOptions(): any {
|
||||
function getSendEmailOptions({ content = {}, routing = {}, transport = {} } = {}) {
|
||||
return {
|
||||
content: {
|
||||
...content,
|
||||
message: 'a message',
|
||||
subject: 'a subject',
|
||||
},
|
||||
routing: {
|
||||
...routing,
|
||||
from: 'fred@example.com',
|
||||
to: ['jim@example.com'],
|
||||
cc: ['bob@example.com', 'robert@example.com'],
|
||||
bcc: [],
|
||||
},
|
||||
transport: {
|
||||
...transport,
|
||||
service: 'whatever',
|
||||
user: 'elastic',
|
||||
password: 'changeme',
|
||||
|
|
|
@ -43,13 +43,13 @@ export interface Content {
|
|||
}
|
||||
|
||||
// send an email
|
||||
export async function sendEmail(logger: Logger, options: SendEmailOptions): Promise<any> {
|
||||
export async function sendEmail(logger: Logger, options: SendEmailOptions): Promise<unknown> {
|
||||
const { transport, routing, content } = options;
|
||||
const { service, host, port, secure, user, password } = transport;
|
||||
const { from, to, cc, bcc } = routing;
|
||||
const { subject, message } = content;
|
||||
|
||||
const transportConfig: Record<string, any> = {};
|
||||
const transportConfig: Record<string, unknown> = {};
|
||||
|
||||
if (user != null && password != null) {
|
||||
transportConfig.auth = {
|
||||
|
|
|
@ -22,7 +22,7 @@ const postPagerdutyMock = postPagerduty as jest.Mock;
|
|||
const ACTION_TYPE_ID = '.pagerduty';
|
||||
|
||||
const services: Services = {
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
|
||||
|
|
|
@ -68,9 +68,8 @@ const ParamsSchema = schema.object(
|
|||
{ validate: validateParams }
|
||||
);
|
||||
|
||||
function validateParams(paramsObject: any): string | void {
|
||||
const params: ActionParamsType = paramsObject;
|
||||
const { timestamp } = params;
|
||||
function validateParams(paramsObject: unknown): string | void {
|
||||
const { timestamp } = paramsObject as ActionParamsType;
|
||||
if (timestamp != null) {
|
||||
try {
|
||||
const date = Date.parse(timestamp);
|
||||
|
@ -218,11 +217,23 @@ async function executor(
|
|||
|
||||
const AcknowledgeOrResolve = new Set([EVENT_ACTION_ACKNOWLEDGE, EVENT_ACTION_RESOLVE]);
|
||||
|
||||
function getBodyForEventAction(actionId: string, params: ActionParamsType): any {
|
||||
function getBodyForEventAction(actionId: string, params: ActionParamsType): unknown {
|
||||
const eventAction = params.eventAction || EVENT_ACTION_TRIGGER;
|
||||
const dedupKey = params.dedupKey || `action:${actionId}`;
|
||||
|
||||
const data: any = {
|
||||
const data: {
|
||||
event_action: ActionParamsType['eventAction'];
|
||||
dedup_key: string;
|
||||
payload?: {
|
||||
summary: string;
|
||||
source: string;
|
||||
severity: string;
|
||||
timestamp?: string;
|
||||
component?: string;
|
||||
group?: string;
|
||||
class?: string;
|
||||
};
|
||||
} = {
|
||||
event_action: eventAction,
|
||||
dedup_key: dedupKey,
|
||||
};
|
||||
|
|
|
@ -91,7 +91,7 @@ describe('execute()', () => {
|
|||
await actionType.executor({
|
||||
actionId,
|
||||
services: {
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
},
|
||||
params: { message: 'message text here', level: 'info' },
|
||||
|
|
|
@ -57,14 +57,14 @@ export const handleCreateIncident = async ({
|
|||
comments &&
|
||||
Array.isArray(comments) &&
|
||||
comments.length > 0 &&
|
||||
mapping.get('comments').actionType !== 'nothing'
|
||||
mapping.get('comments')?.actionType !== 'nothing'
|
||||
) {
|
||||
comments = transformComments(comments, params, ['informationAdded']);
|
||||
res.comments = [
|
||||
...(await createComments(
|
||||
serviceNow,
|
||||
res.incidentId,
|
||||
mapping.get('comments').target,
|
||||
mapping.get('comments')!.target,
|
||||
comments
|
||||
)),
|
||||
];
|
||||
|
@ -103,11 +103,11 @@ export const handleUpdateIncident = async ({
|
|||
comments &&
|
||||
Array.isArray(comments) &&
|
||||
comments.length > 0 &&
|
||||
mapping.get('comments').actionType !== 'nothing'
|
||||
mapping.get('comments')?.actionType !== 'nothing'
|
||||
) {
|
||||
comments = transformComments(comments, params, ['informationAdded']);
|
||||
res.comments = [
|
||||
...(await createComments(serviceNow, incidentId, mapping.get('comments').target, comments)),
|
||||
...(await createComments(serviceNow, incidentId, mapping.get('comments')!.target, comments)),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ export const buildMap = (mapping: MapEntry[]): Mapping => {
|
|||
}, new Map());
|
||||
};
|
||||
|
||||
export const mapParams = (params: any, mapping: Mapping) => {
|
||||
export const mapParams = (params: Record<string, unknown>, mapping: Mapping) => {
|
||||
return Object.keys(params).reduce((prev: KeyAny, curr: string): KeyAny => {
|
||||
const field = mapping.get(curr);
|
||||
if (field) {
|
||||
|
@ -61,11 +61,11 @@ export const prepareFieldsForTransformation = ({
|
|||
defaultPipes = ['informationCreated'],
|
||||
}: PrepareFieldsForTransformArgs): PipedField[] => {
|
||||
return Object.keys(params.incident)
|
||||
.filter(p => mapping.get(p).actionType !== 'nothing')
|
||||
.filter(p => mapping.get(p)!.actionType !== 'nothing')
|
||||
.map(p => ({
|
||||
key: p,
|
||||
value: params.incident[p],
|
||||
actionType: mapping.get(p).actionType,
|
||||
value: params.incident[p] as string,
|
||||
actionType: mapping.get(p)!.actionType,
|
||||
pipes: [...defaultPipes],
|
||||
}))
|
||||
.map(p => ({
|
||||
|
|
|
@ -22,7 +22,7 @@ jest.mock('./action_handlers');
|
|||
const handleIncidentMock = handleIncident as jest.Mock;
|
||||
|
||||
const services: Services = {
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ async function serviceNowExecutor(
|
|||
const { comments, incidentId, ...restParams } = params;
|
||||
|
||||
const mapping = buildMap(configurationMapping);
|
||||
const incident = mapParams(restParams, mapping);
|
||||
const incident = mapParams((restParams as unknown) as Record<string, unknown>, mapping);
|
||||
const serviceNow = new ServiceNow({ url: apiUrl, username, password });
|
||||
|
||||
const handlerInput = {
|
||||
|
|
|
@ -49,14 +49,14 @@ class ServiceNow {
|
|||
}: {
|
||||
url: string;
|
||||
method?: Method;
|
||||
data?: any;
|
||||
data?: unknown;
|
||||
}): Promise<AxiosResponse> {
|
||||
const res = await this.axios(url, { method, data });
|
||||
this._throwIfNotAlive(res.status, res.headers['content-type']);
|
||||
return res;
|
||||
}
|
||||
|
||||
private _patch({ url, data }: { url: string; data: any }): Promise<AxiosResponse> {
|
||||
private _patch({ url, data }: { url: string; data: unknown }): Promise<AxiosResponse> {
|
||||
return this._request({
|
||||
url,
|
||||
method: 'patch',
|
||||
|
|
|
@ -30,10 +30,10 @@ export type CasesConfigurationType = TypeOf<typeof CasesConfigurationSchema>;
|
|||
export type MapEntry = TypeOf<typeof MapEntrySchema>;
|
||||
export type Comment = TypeOf<typeof CommentSchema>;
|
||||
|
||||
export type Mapping = Map<string, any>;
|
||||
export type Mapping = Map<string, Omit<MapEntry, 'source'>>;
|
||||
|
||||
export interface Params extends ExecutorParams {
|
||||
incident: Record<string, any>;
|
||||
incident: Record<string, unknown>;
|
||||
}
|
||||
export interface CreateHandlerArguments {
|
||||
serviceNow: ServiceNow;
|
||||
|
@ -66,7 +66,7 @@ export interface AppendFieldArgs {
|
|||
}
|
||||
|
||||
export interface KeyAny {
|
||||
[index: string]: string;
|
||||
[index: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AppendInformationFieldArgs {
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ActionType, Services, ActionTypeExecutorOptions } from '../types';
|
||||
import {
|
||||
ActionType,
|
||||
Services,
|
||||
ActionTypeExecutorOptions,
|
||||
ActionTypeExecutorResult,
|
||||
} from '../types';
|
||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
||||
import { validateParams, validateSecrets } from '../lib';
|
||||
import { getActionType } from './slack';
|
||||
|
@ -13,7 +18,7 @@ import { actionsConfigMock } from '../actions_config.mock';
|
|||
const ACTION_TYPE_ID = '.slack';
|
||||
|
||||
const services: Services = {
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
|
||||
|
@ -21,7 +26,7 @@ let actionType: ActionType;
|
|||
|
||||
beforeAll(() => {
|
||||
actionType = getActionType({
|
||||
async executor(options: ActionTypeExecutorOptions): Promise<any> {},
|
||||
async executor() {},
|
||||
configurationUtilities: actionsConfigMock.create(),
|
||||
});
|
||||
});
|
||||
|
@ -117,7 +122,7 @@ describe('validateActionTypeSecrets()', () => {
|
|||
|
||||
describe('execute()', () => {
|
||||
beforeAll(() => {
|
||||
async function mockSlackExecutor(options: ActionTypeExecutorOptions): Promise<any> {
|
||||
async function mockSlackExecutor(options: ActionTypeExecutorOptions) {
|
||||
const { params } = options;
|
||||
const { message } = params;
|
||||
if (message == null) throw new Error('message property required in parameter');
|
||||
|
@ -130,7 +135,9 @@ describe('execute()', () => {
|
|||
|
||||
return {
|
||||
text: `slack mockExecutor success: ${message}`,
|
||||
};
|
||||
actionId: '',
|
||||
status: 'ok',
|
||||
} as ActionTypeExecutorResult;
|
||||
}
|
||||
|
||||
actionType = getActionType({
|
||||
|
@ -148,10 +155,12 @@ describe('execute()', () => {
|
|||
params: { message: 'this invocation should succeed' },
|
||||
});
|
||||
expect(response).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"text": "slack mockExecutor success: this invocation should succeed",
|
||||
}
|
||||
`);
|
||||
Object {
|
||||
"actionId": "",
|
||||
"status": "ok",
|
||||
"text": "slack mockExecutor success: this invocation should succeed",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('calls the mock executor with failure', async () => {
|
||||
|
|
|
@ -156,7 +156,7 @@ async function slackExecutor(
|
|||
return successResult(actionId, result);
|
||||
}
|
||||
|
||||
function successResult(actionId: string, data: any): ActionTypeExecutorResult {
|
||||
function successResult(actionId: string, data: unknown): ActionTypeExecutorResult {
|
||||
return { status: 'ok', data, actionId };
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ const axiosRequestMock = axios.request as jest.Mock;
|
|||
const ACTION_TYPE_ID = '.webhook';
|
||||
|
||||
const services: Services = {
|
||||
callCluster: async (path: string, opts: any) => {},
|
||||
callCluster: async (path: string, opts: unknown) => {},
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,7 @@ describe('actionType', () => {
|
|||
|
||||
describe('secrets validation', () => {
|
||||
test('succeeds when secrets is valid', () => {
|
||||
const secrets: Record<string, any> = {
|
||||
const secrets: Record<string, string> = {
|
||||
user: 'bob',
|
||||
password: 'supersecret',
|
||||
};
|
||||
|
@ -60,20 +60,18 @@ describe('secrets validation', () => {
|
|||
});
|
||||
|
||||
test('succeeds when basic authentication credentials are omitted', () => {
|
||||
expect(() => {
|
||||
validateSecrets(actionType, {}).toEqual({});
|
||||
});
|
||||
expect(validateSecrets(actionType, {})).toEqual({ password: null, user: null });
|
||||
});
|
||||
});
|
||||
|
||||
describe('config validation', () => {
|
||||
const defaultValues: Record<string, any> = {
|
||||
const defaultValues: Record<string, string | null> = {
|
||||
headers: null,
|
||||
method: 'post',
|
||||
};
|
||||
|
||||
test('config validation passes when only required fields are provided', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, string> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
};
|
||||
expect(validateConfig(actionType, config)).toEqual({
|
||||
|
@ -84,7 +82,7 @@ describe('config validation', () => {
|
|||
|
||||
test('config validation passes when valid methods are provided', () => {
|
||||
['post', 'put'].forEach(method => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, string> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
method,
|
||||
};
|
||||
|
@ -96,7 +94,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('should validate and throw error when method on config is invalid', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, string> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
method: 'https',
|
||||
};
|
||||
|
@ -110,7 +108,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('config validation passes when a url is specified', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, string> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
};
|
||||
expect(validateConfig(actionType, config)).toEqual({
|
||||
|
@ -120,6 +118,8 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('config validation passes when valid headers are provided', () => {
|
||||
// any for testing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const config: Record<string, any> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
headers: {
|
||||
|
@ -133,7 +133,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('should validate and throw error when headers on config is invalid', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, string> = {
|
||||
url: 'http://mylisteningserver:9200/endpoint',
|
||||
headers: 'application/json',
|
||||
};
|
||||
|
@ -147,6 +147,8 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('config validation passes when kibana config whitelists the url', () => {
|
||||
// any for testing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const config: Record<string, any> = {
|
||||
url: 'http://mylisteningserver.com:9200/endpoint',
|
||||
headers: {
|
||||
|
@ -171,6 +173,8 @@ describe('config validation', () => {
|
|||
},
|
||||
});
|
||||
|
||||
// any for testing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const config: Record<string, any> = {
|
||||
url: 'http://mylisteningserver.com:9200/endpoint',
|
||||
headers: {
|
||||
|
@ -188,12 +192,12 @@ describe('config validation', () => {
|
|||
|
||||
describe('params validation', () => {
|
||||
test('param validation passes when no fields are provided as none are required', () => {
|
||||
const params: Record<string, any> = {};
|
||||
const params: Record<string, string> = {};
|
||||
expect(validateParams(actionType, params)).toEqual({});
|
||||
});
|
||||
|
||||
test('params validation passes when a valid body is provided', () => {
|
||||
const params: Record<string, any> = {
|
||||
const params: Record<string, string> = {
|
||||
body: 'count: {{ctx.payload.hits.total}}',
|
||||
};
|
||||
expect(validateParams(actionType, params)).toEqual({
|
||||
|
|
|
@ -160,7 +160,7 @@ export async function executor(
|
|||
}
|
||||
|
||||
// Action Executor Result w/ internationalisation
|
||||
function successResult(actionId: string, data: any): ActionTypeExecutorResult {
|
||||
function successResult(actionId: string, data: unknown): ActionTypeExecutorResult {
|
||||
return { status: 'ok', data, actionId };
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { configSchema } from './config';
|
|||
|
||||
describe('config validation', () => {
|
||||
test('action defaults', () => {
|
||||
const config: Record<string, any> = {};
|
||||
const config: Record<string, unknown> = {};
|
||||
expect(configSchema.validate(config)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": true,
|
||||
|
@ -23,7 +23,7 @@ describe('config validation', () => {
|
|||
});
|
||||
|
||||
test('action with preconfigured actions', () => {
|
||||
const config: Record<string, any> = {
|
||||
const config: Record<string, unknown> = {
|
||||
preconfigured: [
|
||||
{
|
||||
id: 'my-slack1',
|
||||
|
|
|
@ -9,6 +9,7 @@ import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../legacy/common/const
|
|||
export const PLUGIN = {
|
||||
ID: 'actions',
|
||||
MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType, // TODO: supposed to be changed up on requirements
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getI18nName: (i18n: any): string =>
|
||||
i18n.translate('xpack.actions.appName', {
|
||||
defaultMessage: 'Actions',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract } from '../../../../src/core/server';
|
||||
import { SavedObjectsClientContract, KibanaRequest } from '../../../../src/core/server';
|
||||
import { TaskManagerStartContract } from '../../task_manager/server';
|
||||
import {
|
||||
GetBasePathFunction,
|
||||
|
@ -15,7 +15,7 @@ import {
|
|||
|
||||
interface CreateExecuteFunctionOptions {
|
||||
taskManager: TaskManagerStartContract;
|
||||
getScopedSavedObjectsClient: (request: any) => SavedObjectsClientContract;
|
||||
getScopedSavedObjectsClient: (request: KibanaRequest) => SavedObjectsClientContract;
|
||||
getBasePath: GetBasePathFunction;
|
||||
isESOUsingEphemeralEncryptionKey: boolean;
|
||||
actionTypeRegistry: ActionTypeRegistryContract;
|
||||
|
@ -24,7 +24,7 @@ interface CreateExecuteFunctionOptions {
|
|||
|
||||
export interface ExecuteOptions {
|
||||
id: string;
|
||||
params: Record<string, any>;
|
||||
params: Record<string, unknown>;
|
||||
spaceId: string;
|
||||
apiKey: string | null;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export function createExecuteFunction({
|
|||
|
||||
// Since we're using API keys and accessing elasticsearch can only be done
|
||||
// via a request, we're faking one with the proper authorization headers.
|
||||
const fakeRequest: any = {
|
||||
const fakeRequest: unknown = {
|
||||
headers: requestHeaders,
|
||||
getBasePath: () => getBasePath(spaceId),
|
||||
path: '/',
|
||||
|
@ -67,7 +67,7 @@ export function createExecuteFunction({
|
|||
},
|
||||
};
|
||||
|
||||
const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest);
|
||||
const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest as KibanaRequest);
|
||||
const actionTypeId = await getActionTypeId(id);
|
||||
|
||||
actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);
|
||||
|
|
|
@ -32,7 +32,7 @@ export interface ActionExecutorContext {
|
|||
export interface ExecuteOptions {
|
||||
actionId: string;
|
||||
request: KibanaRequest;
|
||||
params: Record<string, any>;
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type ActionExecutorContract = PublicMethodsOf<ActionExecutor>;
|
||||
|
@ -93,9 +93,9 @@ export class ActionExecutor {
|
|||
actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);
|
||||
const actionType = actionTypeRegistry.get(actionTypeId);
|
||||
|
||||
let validatedParams: Record<string, any>;
|
||||
let validatedConfig: Record<string, any>;
|
||||
let validatedSecrets: Record<string, any>;
|
||||
let validatedParams: Record<string, unknown>;
|
||||
let validatedConfig: Record<string, unknown>;
|
||||
let validatedSecrets: Record<string, unknown>;
|
||||
|
||||
try {
|
||||
validatedParams = validateParams(actionType, params);
|
||||
|
@ -174,8 +174,8 @@ function actionErrorToMessage(result: ActionTypeExecutorResult): string {
|
|||
interface ActionInfo {
|
||||
actionTypeId: string;
|
||||
name: string;
|
||||
config: any;
|
||||
secrets: any;
|
||||
config: unknown;
|
||||
secrets: unknown;
|
||||
}
|
||||
|
||||
async function getActionInfo(
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
|
||||
export class ExecutorError extends Error {
|
||||
readonly data?: any;
|
||||
readonly data?: unknown;
|
||||
readonly retry: boolean | Date;
|
||||
constructor(message?: string, data?: any, retry: boolean | Date = false) {
|
||||
constructor(message?: string, data?: unknown, retry: boolean | Date = false) {
|
||||
super(message);
|
||||
this.data = data;
|
||||
this.retry = retry;
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
*/
|
||||
|
||||
import { ActionType } from '../types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { LicenseState, ILicenseState } from './license_state';
|
||||
import { licensingMock } from '../../../licensing/server/mocks';
|
||||
import { ILicense } from '../../../licensing/server';
|
||||
|
||||
describe('checkLicense()', () => {
|
||||
let getRawLicense: any;
|
||||
const getRawLicense = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
getRawLicense = jest.fn();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('status is LICENSE_STATUS_INVALID', () => {
|
||||
|
@ -53,7 +53,7 @@ describe('checkLicense()', () => {
|
|||
});
|
||||
|
||||
describe('isLicenseValidForActionType', () => {
|
||||
let license: BehaviorSubject<ILicense>;
|
||||
let license: Subject<ILicense>;
|
||||
let licenseState: ILicenseState;
|
||||
const fooActionType: ActionType = {
|
||||
id: 'foo',
|
||||
|
@ -63,7 +63,7 @@ describe('isLicenseValidForActionType', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
license = new BehaviorSubject(null as any);
|
||||
license = new Subject();
|
||||
licenseState = new LicenseState(license);
|
||||
});
|
||||
|
||||
|
@ -75,7 +75,7 @@ describe('isLicenseValidForActionType', () => {
|
|||
});
|
||||
|
||||
test('should return false when license not available', () => {
|
||||
license.next({ isAvailable: false } as any);
|
||||
license.next(createUnavailableLicense());
|
||||
expect(licenseState.isLicenseValidForActionType(fooActionType)).toEqual({
|
||||
isValid: false,
|
||||
reason: 'unavailable',
|
||||
|
@ -114,7 +114,7 @@ describe('isLicenseValidForActionType', () => {
|
|||
});
|
||||
|
||||
describe('ensureLicenseForActionType()', () => {
|
||||
let license: BehaviorSubject<ILicense>;
|
||||
let license: Subject<ILicense>;
|
||||
let licenseState: ILicenseState;
|
||||
const fooActionType: ActionType = {
|
||||
id: 'foo',
|
||||
|
@ -124,7 +124,7 @@ describe('ensureLicenseForActionType()', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
license = new BehaviorSubject(null as any);
|
||||
license = new Subject();
|
||||
licenseState = new LicenseState(license);
|
||||
});
|
||||
|
||||
|
@ -137,7 +137,7 @@ describe('ensureLicenseForActionType()', () => {
|
|||
});
|
||||
|
||||
test('should throw when license not available', () => {
|
||||
license.next({ isAvailable: false } as any);
|
||||
license.next(createUnavailableLicense());
|
||||
expect(() =>
|
||||
licenseState.ensureLicenseForActionType(fooActionType)
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -175,3 +175,9 @@ describe('ensureLicenseForActionType()', () => {
|
|||
licenseState.ensureLicenseForActionType(fooActionType);
|
||||
});
|
||||
});
|
||||
|
||||
function createUnavailableLicense() {
|
||||
const unavailableLicense = licensingMock.createLicenseMock();
|
||||
unavailableLicense.isAvailable = false;
|
||||
return unavailableLicense;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { ActionExecutorContract } from './action_executor';
|
||||
import { ExecutorError } from './executor_error';
|
||||
import { Logger, CoreStart } from '../../../../../src/core/server';
|
||||
import { Logger, CoreStart, KibanaRequest } from '../../../../../src/core/server';
|
||||
import { RunContext } from '../../../task_manager/server';
|
||||
import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server';
|
||||
import { ActionTypeDisabledError } from './errors';
|
||||
|
@ -60,7 +60,7 @@ export class TaskRunnerFactory {
|
|||
|
||||
return {
|
||||
async run() {
|
||||
const { spaceId, actionTaskParamsId } = taskInstance.params;
|
||||
const { spaceId, actionTaskParamsId } = taskInstance.params as Record<string, string>;
|
||||
const namespace = spaceIdToNamespace(spaceId);
|
||||
|
||||
const {
|
||||
|
@ -78,7 +78,7 @@ export class TaskRunnerFactory {
|
|||
|
||||
// Since we're using API keys and accessing elasticsearch can only be done
|
||||
// via a request, we're faking one with the proper authorization headers.
|
||||
const fakeRequest: any = {
|
||||
const fakeRequest = ({
|
||||
headers: requestHeaders,
|
||||
getBasePath: () => getBasePath(spaceId),
|
||||
path: '/',
|
||||
|
@ -91,7 +91,7 @@ export class TaskRunnerFactory {
|
|||
url: '/',
|
||||
},
|
||||
},
|
||||
};
|
||||
} as unknown) as KibanaRequest;
|
||||
|
||||
let executorResult: ActionTypeExecutorResult;
|
||||
try {
|
||||
|
|
|
@ -49,7 +49,7 @@ test('should validate when there are no individual validators', () => {
|
|||
});
|
||||
|
||||
test('should validate when validators return incoming value', () => {
|
||||
const selfValidator = { validate: (value: any) => value };
|
||||
const selfValidator = { validate: (value: Record<string, unknown>) => value };
|
||||
const actionType: ActionType = {
|
||||
id: 'foo',
|
||||
name: 'bar',
|
||||
|
@ -76,8 +76,8 @@ test('should validate when validators return incoming value', () => {
|
|||
});
|
||||
|
||||
test('should validate when validators return different values', () => {
|
||||
const returnedValue: any = { something: { shaped: 'differently' } };
|
||||
const selfValidator = { validate: (value: any) => returnedValue };
|
||||
const returnedValue = { something: { shaped: 'differently' } };
|
||||
const selfValidator = { validate: () => returnedValue };
|
||||
const actionType: ActionType = {
|
||||
id: 'foo',
|
||||
name: 'bar',
|
||||
|
@ -105,7 +105,7 @@ test('should validate when validators return different values', () => {
|
|||
|
||||
test('should throw with expected error when validators fail', () => {
|
||||
const erroringValidator = {
|
||||
validate: (value: any) => {
|
||||
validate: () => {
|
||||
throw new Error('test error');
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
import Boom from 'boom';
|
||||
import { ActionType } from '../types';
|
||||
|
||||
export function validateParams(actionType: ActionType, value: any) {
|
||||
export function validateParams(actionType: ActionType, value: unknown) {
|
||||
return validateWithSchema(actionType, 'params', value);
|
||||
}
|
||||
|
||||
export function validateConfig(actionType: ActionType, value: any) {
|
||||
export function validateConfig(actionType: ActionType, value: unknown) {
|
||||
return validateWithSchema(actionType, 'config', value);
|
||||
}
|
||||
|
||||
export function validateSecrets(actionType: ActionType, value: any) {
|
||||
export function validateSecrets(actionType: ActionType, value: unknown) {
|
||||
return validateWithSchema(actionType, 'secrets', value);
|
||||
}
|
||||
|
||||
|
@ -24,33 +24,40 @@ type ValidKeys = 'params' | 'config' | 'secrets';
|
|||
function validateWithSchema(
|
||||
actionType: ActionType,
|
||||
key: ValidKeys,
|
||||
value: any
|
||||
): Record<string, any> {
|
||||
if (actionType.validate == null) return value;
|
||||
value: unknown
|
||||
): Record<string, unknown> {
|
||||
if (actionType.validate) {
|
||||
let name;
|
||||
try {
|
||||
switch (key) {
|
||||
case 'params':
|
||||
name = 'action params';
|
||||
if (actionType.validate.params) {
|
||||
return actionType.validate.params.validate(value);
|
||||
}
|
||||
break;
|
||||
case 'config':
|
||||
name = 'action type config';
|
||||
if (actionType.validate.config) {
|
||||
return actionType.validate.config.validate(value);
|
||||
}
|
||||
|
||||
let name;
|
||||
try {
|
||||
switch (key) {
|
||||
case 'params':
|
||||
name = 'action params';
|
||||
if (actionType.validate.params == null) return value;
|
||||
return actionType.validate.params.validate(value);
|
||||
|
||||
case 'config':
|
||||
name = 'action type config';
|
||||
if (actionType.validate.config == null) return value;
|
||||
return actionType.validate.config.validate(value);
|
||||
|
||||
case 'secrets':
|
||||
name = 'action type secrets';
|
||||
if (actionType.validate.secrets == null) return value;
|
||||
return actionType.validate.secrets.validate(value);
|
||||
break;
|
||||
case 'secrets':
|
||||
name = 'action type secrets';
|
||||
if (actionType.validate.secrets) {
|
||||
return actionType.validate.secrets.validate(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// should never happen, but left here for future-proofing
|
||||
throw new Error(`invalid actionType validate key: ${key}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// we can't really i18n this yet, since the err.message isn't i18n'd itself
|
||||
throw Boom.badRequest(`error validating ${name}: ${err.message}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// we can't really i18n this yet, since the err.message isn't i18n'd itself
|
||||
throw Boom.badRequest(`error validating ${name}: ${err.message}`);
|
||||
}
|
||||
|
||||
// should never happen, but left here for future-proofing
|
||||
throw new Error(`invalid actionType validate key: ${key}`);
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { PluginInitializerContext } from '../../../../src/core/server';
|
||||
import { PluginInitializerContext, RequestHandlerContext } from '../../../../src/core/server';
|
||||
import { coreMock, httpServerMock } from '../../../../src/core/server/mocks';
|
||||
import { licensingMock } from '../../licensing/server/mocks';
|
||||
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
|
||||
|
@ -72,7 +72,9 @@ describe('Actions Plugin', () => {
|
|||
});
|
||||
|
||||
it('should log warning when Encrypted Saved Objects plugin is using an ephemeral encryption key', async () => {
|
||||
await plugin.setup(coreSetup, pluginsSetup);
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
expect(pluginsSetup.encryptedSavedObjects.usingEphemeralEncryptionKey).toEqual(true);
|
||||
expect(context.logger.get().warn).toHaveBeenCalledWith(
|
||||
'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
|
@ -81,7 +83,9 @@ describe('Actions Plugin', () => {
|
|||
|
||||
describe('routeHandlerContext.getActionsClient()', () => {
|
||||
it('should not throw error when ESO plugin not using a generated key', async () => {
|
||||
await plugin.setup(coreSetup, {
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await plugin.setup(coreSetup as any, {
|
||||
...pluginsSetup,
|
||||
encryptedSavedObjects: {
|
||||
...pluginsSetup.encryptedSavedObjects,
|
||||
|
@ -93,8 +97,8 @@ describe('Actions Plugin', () => {
|
|||
const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0];
|
||||
expect(handler[0]).toEqual('actions');
|
||||
|
||||
const actionsContextHandler = (await handler[1](
|
||||
{
|
||||
const actionsContextHandler = ((await handler[1](
|
||||
({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client: {},
|
||||
|
@ -103,32 +107,34 @@ describe('Actions Plugin', () => {
|
|||
adminClient: jest.fn(),
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
} as unknown) as RequestHandlerContext,
|
||||
httpServerMock.createKibanaRequest(),
|
||||
httpServerMock.createResponseFactory()
|
||||
)) as any;
|
||||
actionsContextHandler.getActionsClient();
|
||||
)) as unknown) as RequestHandlerContext['actions'];
|
||||
actionsContextHandler!.getActionsClient();
|
||||
});
|
||||
|
||||
it('should throw error when ESO plugin using a generated key', async () => {
|
||||
await plugin.setup(coreSetup, pluginsSetup);
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
|
||||
expect(coreSetup.http.registerRouteHandlerContext).toHaveBeenCalledTimes(1);
|
||||
const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0];
|
||||
expect(handler[0]).toEqual('actions');
|
||||
|
||||
const actionsContextHandler = (await handler[1](
|
||||
{
|
||||
const actionsContextHandler = ((await handler[1](
|
||||
({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client: {},
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
} as unknown) as RequestHandlerContext,
|
||||
httpServerMock.createKibanaRequest(),
|
||||
httpServerMock.createResponseFactory()
|
||||
)) as any;
|
||||
expect(() => actionsContextHandler.getActionsClient()).toThrowErrorMatchingInlineSnapshot(
|
||||
)) as unknown) as RequestHandlerContext['actions'];
|
||||
expect(() => actionsContextHandler!.getActionsClient()).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
);
|
||||
});
|
||||
|
@ -144,13 +150,17 @@ describe('Actions Plugin', () => {
|
|||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
setup = await plugin.setup(coreSetup, pluginsSetup);
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
setup = await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
});
|
||||
|
||||
it('should throw error when license type is invalid', async () => {
|
||||
expect(() =>
|
||||
setup.registerType({
|
||||
...sampleActionType,
|
||||
// we're faking an invalid value, this requires stripping the typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
minimumLicenseRequired: 'foo' as any,
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"\\"foo\\" is not a valid license type"`);
|
||||
|
@ -211,7 +221,9 @@ describe('Actions Plugin', () => {
|
|||
|
||||
describe('getActionsClientWithRequest()', () => {
|
||||
it('should not throw error when ESO plugin not using a generated key', async () => {
|
||||
await plugin.setup(coreSetup, {
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await plugin.setup(coreSetup as any, {
|
||||
...pluginsSetup,
|
||||
encryptedSavedObjects: {
|
||||
...pluginsSetup.encryptedSavedObjects,
|
||||
|
@ -224,7 +236,9 @@ describe('Actions Plugin', () => {
|
|||
});
|
||||
|
||||
it('should throw error when ESO plugin using generated key', async () => {
|
||||
await plugin.setup(coreSetup, pluginsSetup);
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
const pluginStart = plugin.start(coreStart, pluginsStart);
|
||||
|
||||
expect(pluginsSetup.encryptedSavedObjects.usingEphemeralEncryptionKey).toEqual(true);
|
||||
|
|
|
@ -117,7 +117,10 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
this.preconfiguredActions = [];
|
||||
}
|
||||
|
||||
public async setup(core: CoreSetup, plugins: ActionsPluginsSetup): Promise<PluginSetupContract> {
|
||||
public async setup(
|
||||
core: CoreSetup<ActionsPluginsStart>,
|
||||
plugins: ActionsPluginsSetup
|
||||
): Promise<PluginSetupContract> {
|
||||
this.licenseState = new LicenseState(plugins.licensing.license$);
|
||||
this.isESOUsingEphemeralEncryptionKey =
|
||||
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
|
||||
|
@ -182,7 +185,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
|
||||
const usageCollection = plugins.usageCollection;
|
||||
if (usageCollection) {
|
||||
core.getStartServices().then(async ([, startPlugins]: [CoreStart, any, any]) => {
|
||||
core.getStartServices().then(async ([, startPlugins]) => {
|
||||
registerActionsUsageCollector(usageCollection, startPlugins.taskManager);
|
||||
|
||||
initializeActionsTelemetry(
|
||||
|
@ -299,7 +302,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
|
||||
private createRouteHandlerContext = (
|
||||
defaultKibanaIndex: string
|
||||
): IContextProvider<RequestHandler<any, any, any>, 'actions'> => {
|
||||
): IContextProvider<RequestHandler<unknown, unknown, unknown>, 'actions'> => {
|
||||
const { actionTypeRegistry, isESOUsingEphemeralEncryptionKey, preconfiguredActions } = this;
|
||||
|
||||
return async function actionsRouteHandlerContext(context, request) {
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
import { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server';
|
||||
import { identity } from 'lodash';
|
||||
import { httpServerMock } from '../../../../../src/core/server/mocks';
|
||||
import { ActionType } from '../../common';
|
||||
import { ActionsClientMock, actionsClientMock } from '../actions_client.mock';
|
||||
|
||||
export function mockHandlerArguments(
|
||||
{ actionsClient, listTypes: listTypesRes = [] }: any,
|
||||
req: any,
|
||||
{
|
||||
actionsClient = actionsClientMock.create(),
|
||||
listTypes: listTypesRes = [],
|
||||
}: { actionsClient?: ActionsClientMock; listTypes?: ActionType[] },
|
||||
req: unknown,
|
||||
res?: Array<MethodKeysOf<KibanaResponseFactory>>
|
||||
): [RequestHandlerContext, KibanaRequest<any, any, any, any>, KibanaResponseFactory] {
|
||||
): [RequestHandlerContext, KibanaRequest<unknown, unknown, unknown>, KibanaResponseFactory] {
|
||||
const listTypes = jest.fn(() => listTypesRes);
|
||||
return [
|
||||
({
|
||||
|
@ -31,7 +36,7 @@ export function mockHandlerArguments(
|
|||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext,
|
||||
req as KibanaRequest<any, any, any, any>,
|
||||
req as KibanaRequest<unknown, unknown, unknown>,
|
||||
mockResponseFactory(res),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ 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(),
|
||||
|
@ -40,10 +41,11 @@ describe('createActionRoute', () => {
|
|||
name: 'My name',
|
||||
actionTypeId: 'abc',
|
||||
config: { foo: true },
|
||||
isPreconfigured: false,
|
||||
};
|
||||
const actionsClient = {
|
||||
create: jest.fn().mockResolvedValueOnce(createResult),
|
||||
};
|
||||
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.create.mockResolvedValueOnce(createResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -89,16 +91,16 @@ describe('createActionRoute', () => {
|
|||
|
||||
const [, handler] = router.post.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
create: jest.fn().mockResolvedValueOnce({
|
||||
id: '1',
|
||||
name: 'My name',
|
||||
actionTypeId: 'abc',
|
||||
config: { foo: true },
|
||||
}),
|
||||
};
|
||||
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, {});
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
|
@ -117,16 +119,16 @@ describe('createActionRoute', () => {
|
|||
|
||||
const [, handler] = router.post.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
create: jest.fn().mockResolvedValueOnce({
|
||||
id: '1',
|
||||
name: 'My name',
|
||||
actionTypeId: 'abc',
|
||||
config: { foo: true },
|
||||
}),
|
||||
};
|
||||
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, {});
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {});
|
||||
|
||||
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
|
||||
|
||||
|
@ -141,9 +143,8 @@ describe('createActionRoute', () => {
|
|||
|
||||
const [, handler] = router.post.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
create: jest.fn().mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid')),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.create.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok', 'forbidden']);
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@ export const createActionRoute = (router: IRouter, licenseState: ILicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, TypeOf<typeof bodySchema>, any>,
|
||||
req: KibanaRequest<unknown, unknown, TypeOf<typeof bodySchema>>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
|
||||
if (!context.actions) {
|
||||
|
|
|
@ -8,6 +8,7 @@ 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(),
|
||||
|
@ -35,9 +36,8 @@ describe('deleteActionRoute', () => {
|
|||
}
|
||||
`);
|
||||
|
||||
const actionsClient = {
|
||||
delete: jest.fn().mockResolvedValueOnce({}),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.delete.mockResolvedValueOnce({});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -71,13 +71,15 @@ describe('deleteActionRoute', () => {
|
|||
|
||||
const [, handler] = router.delete.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
delete: jest.fn().mockResolvedValueOnce({}),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.delete.mockResolvedValueOnce({});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(actionsClient, {
|
||||
params: { id: '1' },
|
||||
});
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
}
|
||||
);
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
|
@ -96,13 +98,15 @@ describe('deleteActionRoute', () => {
|
|||
|
||||
const [, handler] = router.delete.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
delete: jest.fn().mockResolvedValueOnce({}),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.delete.mockResolvedValueOnce({});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(actionsClient, {
|
||||
id: '1',
|
||||
});
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
{
|
||||
id: '1',
|
||||
}
|
||||
);
|
||||
|
||||
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
|
||||
|
||||
|
|
|
@ -37,9 +37,9 @@ export const deleteActionRoute = (router: IRouter, licenseState: ILicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.actions) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
|
||||
|
|
|
@ -43,9 +43,9 @@ export const executeActionRoute = (
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, TypeOf<typeof bodySchema>, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, TypeOf<typeof bodySchema>>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
const { params } = req.body;
|
||||
const { id } = req.params;
|
||||
|
|
|
@ -9,6 +9,7 @@ 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(),
|
||||
|
@ -41,10 +42,11 @@ describe('getActionRoute', () => {
|
|||
actionTypeId: '2',
|
||||
name: 'action name',
|
||||
config: {},
|
||||
isPreconfigured: false,
|
||||
};
|
||||
const actionsClient = {
|
||||
get: jest.fn().mockResolvedValueOnce(getResult),
|
||||
};
|
||||
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.get.mockResolvedValueOnce(getResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -60,6 +62,7 @@ describe('getActionRoute', () => {
|
|||
"actionTypeId": "2",
|
||||
"config": Object {},
|
||||
"id": "1",
|
||||
"isPreconfigured": false,
|
||||
"name": "action name",
|
||||
},
|
||||
}
|
||||
|
@ -81,17 +84,17 @@ describe('getActionRoute', () => {
|
|||
|
||||
const [, handler] = router.get.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
get: jest.fn().mockResolvedValueOnce({
|
||||
id: '1',
|
||||
actionTypeId: '2',
|
||||
name: 'action name',
|
||||
config: {},
|
||||
}),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.get.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
actionTypeId: '2',
|
||||
name: 'action name',
|
||||
config: {},
|
||||
isPreconfigured: false,
|
||||
});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
actionsClient,
|
||||
{ actionsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
},
|
||||
|
@ -115,17 +118,17 @@ describe('getActionRoute', () => {
|
|||
|
||||
const [, handler] = router.get.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
get: jest.fn().mockResolvedValueOnce({
|
||||
id: '1',
|
||||
actionTypeId: '2',
|
||||
name: 'action name',
|
||||
config: {},
|
||||
}),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.get.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
actionTypeId: '2',
|
||||
name: 'action name',
|
||||
config: {},
|
||||
isPreconfigured: false,
|
||||
});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
actionsClient,
|
||||
{ actionsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
},
|
||||
|
|
|
@ -32,9 +32,9 @@ export const getActionRoute = (router: IRouter, licenseState: ILicenseState) =>
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.actions) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
|
||||
|
|
|
@ -9,6 +9,7 @@ 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(),
|
||||
|
@ -36,9 +37,8 @@ describe('getAllActionRoute', () => {
|
|||
}
|
||||
`);
|
||||
|
||||
const actionsClient = {
|
||||
getAll: jest.fn().mockResolvedValueOnce([]),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.getAll.mockResolvedValueOnce([]);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
|
||||
|
||||
|
@ -72,9 +72,8 @@ describe('getAllActionRoute', () => {
|
|||
}
|
||||
`);
|
||||
|
||||
const actionsClient = {
|
||||
getAll: jest.fn().mockResolvedValueOnce([]),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.getAll.mockResolvedValueOnce([]);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
|
||||
|
||||
|
@ -104,9 +103,8 @@ describe('getAllActionRoute', () => {
|
|||
}
|
||||
`);
|
||||
|
||||
const actionsClient = {
|
||||
getAll: jest.fn().mockResolvedValueOnce([]),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.getAll.mockResolvedValueOnce([]);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']);
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ export const getAllActionRoute = (router: IRouter, licenseState: ILicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, any, any>,
|
||||
req: KibanaRequest<unknown, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.actions) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
|
||||
|
|
|
@ -9,6 +9,7 @@ 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 { LicenseType } from '../../../../plugins/licensing/server';
|
||||
|
||||
jest.mock('../lib/verify_api_access.ts', () => ({
|
||||
verifyApiAccess: jest.fn(),
|
||||
|
@ -41,6 +42,9 @@ describe('listActionTypesRoute', () => {
|
|||
id: '1',
|
||||
name: 'name',
|
||||
enabled: true,
|
||||
enabledInConfig: true,
|
||||
enabledInLicense: true,
|
||||
minimumLicenseRequired: 'gold' as LicenseType,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -51,7 +55,10 @@ describe('listActionTypesRoute', () => {
|
|||
"body": Array [
|
||||
Object {
|
||||
"enabled": true,
|
||||
"enabledInConfig": true,
|
||||
"enabledInLicense": true,
|
||||
"id": "1",
|
||||
"minimumLicenseRequired": "gold",
|
||||
"name": "name",
|
||||
},
|
||||
],
|
||||
|
@ -87,6 +94,9 @@ describe('listActionTypesRoute', () => {
|
|||
id: '1',
|
||||
name: 'name',
|
||||
enabled: true,
|
||||
enabledInConfig: true,
|
||||
enabledInLicense: true,
|
||||
minimumLicenseRequired: 'gold' as LicenseType,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -129,6 +139,9 @@ describe('listActionTypesRoute', () => {
|
|||
id: '1',
|
||||
name: 'name',
|
||||
enabled: true,
|
||||
enabledInConfig: true,
|
||||
enabledInLicense: true,
|
||||
minimumLicenseRequired: 'gold' as LicenseType,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ export const listActionTypesRoute = (router: IRouter, licenseState: ILicenseStat
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, any, any>,
|
||||
req: KibanaRequest<unknown, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.actions) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
|
||||
|
|
|
@ -8,6 +8,7 @@ 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(),
|
||||
|
@ -40,11 +41,11 @@ describe('updateActionRoute', () => {
|
|||
actionTypeId: 'my-action-type-id',
|
||||
name: 'My name',
|
||||
config: { foo: true },
|
||||
isPreconfigured: false,
|
||||
};
|
||||
|
||||
const actionsClient = {
|
||||
update: jest.fn().mockResolvedValueOnce(updateResult),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.update.mockResolvedValueOnce(updateResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -97,11 +98,11 @@ describe('updateActionRoute', () => {
|
|||
actionTypeId: 'my-action-type-id',
|
||||
name: 'My name',
|
||||
config: { foo: true },
|
||||
isPreconfigured: false,
|
||||
};
|
||||
|
||||
const actionsClient = {
|
||||
update: jest.fn().mockResolvedValueOnce(updateResult),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.update.mockResolvedValueOnce(updateResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -140,11 +141,11 @@ describe('updateActionRoute', () => {
|
|||
actionTypeId: 'my-action-type-id',
|
||||
name: 'My name',
|
||||
config: { foo: true },
|
||||
isPreconfigured: false,
|
||||
};
|
||||
|
||||
const actionsClient = {
|
||||
update: jest.fn().mockResolvedValueOnce(updateResult),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.update.mockResolvedValueOnce(updateResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ actionsClient },
|
||||
|
@ -174,9 +175,8 @@ describe('updateActionRoute', () => {
|
|||
|
||||
const [, handler] = router.put.mock.calls[0];
|
||||
|
||||
const actionsClient = {
|
||||
update: jest.fn().mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid')),
|
||||
};
|
||||
const actionsClient = actionsClientMock.create();
|
||||
actionsClient.update.mockRejectedValue(new ActionTypeDisabledError('Fail', 'license_invalid'));
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ actionsClient }, { params: {}, body: {} }, [
|
||||
'ok',
|
||||
|
|
|
@ -39,9 +39,9 @@ export const updateActionRoute = (router: IRouter, licenseState: ILicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, TypeOf<typeof bodySchema>, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, TypeOf<typeof bodySchema>>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.actions) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' });
|
||||
|
|
|
@ -4,20 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract, SavedObjectAttributes } from '../../../../src/core/server';
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectAttributes,
|
||||
KibanaRequest,
|
||||
} from '../../../../src/core/server';
|
||||
import { ActionTypeRegistry } from './action_type_registry';
|
||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||
import { ActionsClient } from './actions_client';
|
||||
import { LicenseType } from '../../licensing/common/types';
|
||||
|
||||
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
|
||||
export type GetServicesFunction = (request: any) => Services;
|
||||
export type GetServicesFunction = (request: KibanaRequest) => Services;
|
||||
export type ActionTypeRegistryContract = PublicMethodsOf<ActionTypeRegistry>;
|
||||
export type GetBasePathFunction = (spaceId?: string) => string;
|
||||
export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
|
||||
|
||||
export interface Services {
|
||||
callCluster(path: string, opts: any): Promise<any>;
|
||||
callCluster(path: string, opts: unknown): Promise<unknown>;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
}
|
||||
|
||||
|
@ -45,8 +49,12 @@ export interface ActionsConfigType {
|
|||
export interface ActionTypeExecutorOptions {
|
||||
actionId: string;
|
||||
services: Services;
|
||||
// This will have to remain `any` until we can extend Action Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
config: Record<string, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
secrets: Record<string, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: Record<string, any>;
|
||||
}
|
||||
|
||||
|
@ -54,11 +62,15 @@ export interface ActionResult {
|
|||
id: string;
|
||||
actionTypeId: string;
|
||||
name: string;
|
||||
// This will have to remain `any` until we can extend Action Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
config?: Record<string, any>;
|
||||
isPreconfigured: boolean;
|
||||
}
|
||||
|
||||
export interface PreConfiguredAction extends ActionResult {
|
||||
// This will have to remain `any` until we can extend Action Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
secrets: Record<string, any>;
|
||||
}
|
||||
|
||||
|
@ -72,6 +84,8 @@ export interface ActionTypeExecutorResult {
|
|||
status: 'ok' | 'error';
|
||||
message?: string;
|
||||
serviceMessage?: string;
|
||||
// This will have to remain `any` until we can extend Action Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data?: any;
|
||||
retry?: null | boolean | Date;
|
||||
}
|
||||
|
@ -82,7 +96,7 @@ export type ExecutorType = (
|
|||
) => Promise<ActionTypeExecutorResult | null | undefined | void>;
|
||||
|
||||
interface ValidatorType {
|
||||
validate<T>(value: any): any;
|
||||
validate(value: unknown): Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type ActionTypeCreator = (config?: ActionsConfigType) => ActionType;
|
||||
|
@ -108,6 +122,13 @@ export interface RawAction extends SavedObjectAttributes {
|
|||
|
||||
export interface ActionTaskParams extends SavedObjectAttributes {
|
||||
actionId: string;
|
||||
// Saved Objects won't allow us to enforce unknown rather than any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: Record<string, any>;
|
||||
apiKey?: string;
|
||||
}
|
||||
|
||||
export interface ActionTaskExecutorParams {
|
||||
spaceId: string;
|
||||
actionTaskParamsId: string;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ export async function getTotalCount(callCluster: APICaller, kibanaIndex: string)
|
|||
0
|
||||
),
|
||||
countByType: Object.keys(searchResult.aggregations.byActionTypeId.value.types).reduce(
|
||||
// ES DSL aggregations are returned as `any` by callCluster
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(obj: any, key: string) => ({
|
||||
...obj,
|
||||
[key.replace('.', '__')]: searchResult.aggregations.byActionTypeId.value.types[key],
|
||||
|
|
|
@ -28,6 +28,8 @@ export interface Alert {
|
|||
consumer: string;
|
||||
schedule: IntervalSchedule;
|
||||
actions: AlertAction[];
|
||||
// This will have to remain `any` until we can extend Alert Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: Record<string, any>;
|
||||
scheduledTaskId?: string;
|
||||
createdBy: string | null;
|
||||
|
|
|
@ -231,7 +231,7 @@ function alertTypeWithVariables(id: string, context: string, state: string): Ale
|
|||
name: `${id}-name`,
|
||||
actionGroups: [],
|
||||
defaultActionGroupId: id,
|
||||
executor: (params: any): any => {},
|
||||
async executor() {},
|
||||
};
|
||||
|
||||
if (!context && !state) {
|
||||
|
|
|
@ -77,7 +77,7 @@ export class AlertTypeRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
function normalizedActionVariables(actionVariables: any) {
|
||||
function normalizedActionVariables(actionVariables: AlertType['actionVariables']) {
|
||||
return {
|
||||
context: actionVariables?.context ?? [],
|
||||
state: actionVariables?.state ?? [],
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
import { AlertsClient } from './alerts_client';
|
||||
|
||||
type Schema = PublicMethodsOf<AlertsClient>;
|
||||
export type AlertsClientMock = jest.Mocked<Schema>;
|
||||
|
||||
const createAlertsClientMock = () => {
|
||||
const mocked: jest.Mocked<Schema> = {
|
||||
const mocked: AlertsClientMock = {
|
||||
create: jest.fn(),
|
||||
get: jest.fn(),
|
||||
getAlertState: jest.fn(),
|
||||
|
@ -27,6 +28,8 @@ const createAlertsClientMock = () => {
|
|||
return mocked;
|
||||
};
|
||||
|
||||
export const alertsClientMock = {
|
||||
export const alertsClientMock: {
|
||||
create: () => AlertsClientMock;
|
||||
} = {
|
||||
create: createAlertsClientMock,
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import uuid from 'uuid';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { AlertsClient } from './alerts_client';
|
||||
import { AlertsClient, CreateOptions } from './alerts_client';
|
||||
import { savedObjectsClientMock, loggingServiceMock } from '../../../../src/core/server/mocks';
|
||||
import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock';
|
||||
import { alertTypeRegistryMock } from './alert_type_registry.mock';
|
||||
|
@ -45,6 +45,7 @@ beforeEach(() => {
|
|||
});
|
||||
|
||||
const mockedDate = new Date('2019-02-12T21:01:22.479Z');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).Date = class Date {
|
||||
constructor() {
|
||||
return mockedDate;
|
||||
|
@ -54,7 +55,7 @@ const mockedDate = new Date('2019-02-12T21:01:22.479Z');
|
|||
}
|
||||
};
|
||||
|
||||
function getMockData(overwrites: Record<string, any> = {}) {
|
||||
function getMockData(overwrites: Record<string, unknown> = {}): CreateOptions['data'] {
|
||||
return {
|
||||
enabled: true,
|
||||
name: 'abc',
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
IntervalSchedule,
|
||||
SanitizedAlert,
|
||||
AlertTaskState,
|
||||
RawAlertAction,
|
||||
} from './types';
|
||||
import { validateAlertTypeParams } from './lib';
|
||||
import {
|
||||
|
@ -83,7 +84,7 @@ export interface FindResult {
|
|||
data: SanitizedAlert[];
|
||||
}
|
||||
|
||||
interface CreateOptions {
|
||||
export interface CreateOptions {
|
||||
data: Omit<
|
||||
Alert,
|
||||
| 'id'
|
||||
|
@ -109,7 +110,7 @@ interface UpdateOptions {
|
|||
tags: string[];
|
||||
schedule: IntervalSchedule;
|
||||
actions: NormalizedAlertAction[];
|
||||
params: Record<string, any>;
|
||||
params: Record<string, unknown>;
|
||||
throttle: string | null;
|
||||
};
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ export class AlertsClient {
|
|||
createdBy: username,
|
||||
updatedBy: username,
|
||||
createdAt: new Date().toISOString(),
|
||||
params: validatedAlertTypeParams,
|
||||
params: validatedAlertTypeParams as RawAlert['params'],
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
};
|
||||
|
@ -337,7 +338,7 @@ export class AlertsClient {
|
|||
...attributes,
|
||||
...data,
|
||||
...apiKeyAttributes,
|
||||
params: validatedAlertTypeParams,
|
||||
params: validatedAlertTypeParams as RawAlert['params'],
|
||||
actions,
|
||||
updatedBy: username,
|
||||
},
|
||||
|
@ -667,7 +668,7 @@ export class AlertsClient {
|
|||
private async denormalizeActions(
|
||||
alertActions: NormalizedAlertAction[]
|
||||
): Promise<{ actions: RawAlert['actions']; references: SavedObjectReference[] }> {
|
||||
const actionMap = new Map<string, any>();
|
||||
const actionMap = new Map<string, unknown>();
|
||||
// map preconfigured actions
|
||||
for (const alertAction of alertActions) {
|
||||
const action = this.preconfiguredActions.find(
|
||||
|
@ -712,8 +713,8 @@ export class AlertsClient {
|
|||
// if action is a save object, than actionTypeId should be under attributes property
|
||||
// if action is a preconfigured, than actionTypeId is the action property
|
||||
const actionTypeId = actionIds.find(actionId => actionId === id)
|
||||
? actionMapValue.attributes.actionTypeId
|
||||
: actionMapValue.actionTypeId;
|
||||
? (actionMapValue as SavedObject<Record<string, string>>).attributes.actionTypeId
|
||||
: (actionMapValue as RawAlertAction).actionTypeId;
|
||||
return {
|
||||
...alertAction,
|
||||
actionRef,
|
||||
|
|
|
@ -11,16 +11,13 @@ import { taskManagerMock } from '../../../plugins/task_manager/server/task_manag
|
|||
import { KibanaRequest } from '../../../../src/core/server';
|
||||
import { loggingServiceMock, savedObjectsClientMock } from '../../../../src/core/server/mocks';
|
||||
import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks';
|
||||
import { AuthenticatedUser } from '../../../plugins/security/public';
|
||||
import { securityMock } from '../../../plugins/security/server/mocks';
|
||||
|
||||
jest.mock('./alerts_client');
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const securityPluginSetup = {
|
||||
authc: {
|
||||
grantAPIKeyAsInternalUser: jest.fn(),
|
||||
getCurrentUser: jest.fn(),
|
||||
},
|
||||
};
|
||||
const securityPluginSetup = securityMock.createSetup();
|
||||
const alertsClientFactoryParams: jest.Mocked<AlertsClientFactoryOpts> = {
|
||||
logger: loggingServiceMock.create().get(),
|
||||
taskManager: taskManagerMock.start(),
|
||||
|
@ -30,7 +27,7 @@ const alertsClientFactoryParams: jest.Mocked<AlertsClientFactoryOpts> = {
|
|||
encryptedSavedObjectsPlugin: encryptedSavedObjectsMock.createStart(),
|
||||
preconfiguredActions: [],
|
||||
};
|
||||
const fakeRequest: Request = {
|
||||
const fakeRequest = ({
|
||||
headers: {},
|
||||
getBasePath: () => '',
|
||||
path: '/',
|
||||
|
@ -44,7 +41,7 @@ const fakeRequest: Request = {
|
|||
},
|
||||
},
|
||||
getSavedObjectsClient: () => savedObjectsClient,
|
||||
} as any;
|
||||
} as unknown) as Request;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -86,12 +83,14 @@ test('getUserName() returns a name when security is enabled', async () => {
|
|||
const factory = new AlertsClientFactory();
|
||||
factory.initialize({
|
||||
...alertsClientFactoryParams,
|
||||
securityPluginSetup: securityPluginSetup as any,
|
||||
securityPluginSetup,
|
||||
});
|
||||
factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
|
||||
const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
|
||||
|
||||
securityPluginSetup.authc.getCurrentUser.mockReturnValueOnce({ username: 'bob' });
|
||||
securityPluginSetup.authc.getCurrentUser.mockReturnValueOnce(({
|
||||
username: 'bob',
|
||||
} as unknown) as AuthenticatedUser);
|
||||
const userNameResult = await constructorCall.getUserName();
|
||||
expect(userNameResult).toEqual('bob');
|
||||
});
|
||||
|
@ -121,7 +120,7 @@ test('createAPIKey() returns an API key when security is enabled', async () => {
|
|||
const factory = new AlertsClientFactory();
|
||||
factory.initialize({
|
||||
...alertsClientFactoryParams,
|
||||
securityPluginSetup: securityPluginSetup as any,
|
||||
securityPluginSetup,
|
||||
});
|
||||
factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
|
||||
const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
|
||||
|
@ -129,11 +128,12 @@ test('createAPIKey() returns an API key when security is enabled', async () => {
|
|||
securityPluginSetup.authc.grantAPIKeyAsInternalUser.mockResolvedValueOnce({
|
||||
api_key: '123',
|
||||
id: 'abc',
|
||||
name: '',
|
||||
});
|
||||
const createAPIKeyResult = await constructorCall.createAPIKey();
|
||||
expect(createAPIKeyResult).toEqual({
|
||||
apiKeysEnabled: true,
|
||||
result: { api_key: '123', id: 'abc' },
|
||||
result: { api_key: '123', id: 'abc', name: '' },
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -141,7 +141,7 @@ test('createAPIKey() throws when security plugin createAPIKey throws an error',
|
|||
const factory = new AlertsClientFactory();
|
||||
factory.initialize({
|
||||
...alertsClientFactoryParams,
|
||||
securityPluginSetup: securityPluginSetup as any,
|
||||
securityPluginSetup,
|
||||
});
|
||||
factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
|
||||
const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
|
||||
|
|
|
@ -9,6 +9,8 @@ import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../legacy/common/const
|
|||
export const PLUGIN = {
|
||||
ID: 'alerting',
|
||||
MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType, // TODO: supposed to be changed up on requirements
|
||||
// all plugins seem to use getI18nName with any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getI18nName: (i18n: any): string =>
|
||||
i18n.translate('xpack.alerting.appName', {
|
||||
defaultMessage: 'Alerting',
|
||||
|
|
|
@ -9,10 +9,10 @@ import { LicenseState } from './license_state';
|
|||
import { licensingMock } from '../../../../plugins/licensing/server/mocks';
|
||||
|
||||
describe('license_state', () => {
|
||||
let getRawLicense: any;
|
||||
const getRawLicense = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
getRawLicense = jest.fn();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('status is LICENSE_STATUS_INVALID', () => {
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
*/
|
||||
|
||||
import Boom from 'boom';
|
||||
import { AlertType } from '../types';
|
||||
import { AlertType, AlertExecutorOptions } from '../types';
|
||||
|
||||
export function validateAlertTypeParams<T extends Record<string, any>>(
|
||||
export function validateAlertTypeParams(
|
||||
alertType: AlertType,
|
||||
params: T
|
||||
): T {
|
||||
params: Record<string, unknown>
|
||||
): AlertExecutorOptions['params'] {
|
||||
const validator = alertType.validate && alertType.validate.params;
|
||||
if (!validator) {
|
||||
return params;
|
||||
return params as AlertExecutorOptions['params'];
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { AlertingPlugin } from './plugin';
|
||||
import { AlertingPlugin, AlertingPluginsSetup, AlertingPluginsStart } from './plugin';
|
||||
import { coreMock } from '../../../../src/core/server/mocks';
|
||||
import { licensingMock } from '../../../plugins/licensing/server/mocks';
|
||||
import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks';
|
||||
import { taskManagerMock } from '../../task_manager/server/mocks';
|
||||
import { eventLogServiceMock } from '../../event_log/server/event_log_service.mock';
|
||||
import { KibanaRequest, CoreSetup } from 'kibana/server';
|
||||
|
||||
describe('Alerting Plugin', () => {
|
||||
describe('setup()', () => {
|
||||
|
@ -20,19 +21,19 @@ describe('Alerting Plugin', () => {
|
|||
const coreSetup = coreMock.createSetup();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
await plugin.setup(
|
||||
{
|
||||
({
|
||||
...coreSetup,
|
||||
http: {
|
||||
...coreSetup.http,
|
||||
route: jest.fn(),
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
} as unknown) as CoreSetup<AlertingPluginsStart, unknown>,
|
||||
({
|
||||
licensing: licensingMock.createSetup(),
|
||||
encryptedSavedObjects: encryptedSavedObjectsSetup,
|
||||
taskManager: taskManagerMock.createSetup(),
|
||||
eventLog: eventLogServiceMock.create(),
|
||||
} as any
|
||||
} as unknown) as AlertingPluginsSetup
|
||||
);
|
||||
|
||||
expect(encryptedSavedObjectsSetup.usingEphemeralEncryptionKey).toEqual(true);
|
||||
|
@ -58,34 +59,34 @@ describe('Alerting Plugin', () => {
|
|||
const coreSetup = coreMock.createSetup();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
await plugin.setup(
|
||||
{
|
||||
({
|
||||
...coreSetup,
|
||||
http: {
|
||||
...coreSetup.http,
|
||||
route: jest.fn(),
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
} as unknown) as CoreSetup<AlertingPluginsStart, unknown>,
|
||||
({
|
||||
licensing: licensingMock.createSetup(),
|
||||
encryptedSavedObjects: encryptedSavedObjectsSetup,
|
||||
taskManager: taskManagerMock.createSetup(),
|
||||
eventLog: eventLogServiceMock.create(),
|
||||
} as any
|
||||
} as unknown) as AlertingPluginsSetup
|
||||
);
|
||||
|
||||
const startContract = plugin.start(
|
||||
coreMock.createStart() as any,
|
||||
{
|
||||
coreMock.createStart() as ReturnType<typeof coreMock.createStart>,
|
||||
({
|
||||
actions: {
|
||||
execute: jest.fn(),
|
||||
getActionsClientWithRequest: jest.fn(),
|
||||
},
|
||||
} as any
|
||||
} as unknown) as AlertingPluginsStart
|
||||
);
|
||||
|
||||
expect(encryptedSavedObjectsSetup.usingEphemeralEncryptionKey).toEqual(true);
|
||||
expect(() =>
|
||||
startContract.getAlertsClientWithRequest({} as any)
|
||||
startContract.getAlertsClientWithRequest({} as KibanaRequest)
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
);
|
||||
|
@ -101,33 +102,33 @@ describe('Alerting Plugin', () => {
|
|||
usingEphemeralEncryptionKey: false,
|
||||
};
|
||||
await plugin.setup(
|
||||
{
|
||||
({
|
||||
...coreSetup,
|
||||
http: {
|
||||
...coreSetup.http,
|
||||
route: jest.fn(),
|
||||
},
|
||||
} as any,
|
||||
{
|
||||
} as unknown) as CoreSetup<AlertingPluginsStart, unknown>,
|
||||
({
|
||||
licensing: licensingMock.createSetup(),
|
||||
encryptedSavedObjects: encryptedSavedObjectsSetup,
|
||||
taskManager: taskManagerMock.createSetup(),
|
||||
eventLog: eventLogServiceMock.create(),
|
||||
} as any
|
||||
} as unknown) as AlertingPluginsSetup
|
||||
);
|
||||
|
||||
const startContract = plugin.start(
|
||||
coreMock.createStart() as any,
|
||||
{
|
||||
coreMock.createStart() as ReturnType<typeof coreMock.createStart>,
|
||||
({
|
||||
actions: {
|
||||
execute: jest.fn(),
|
||||
getActionsClientWithRequest: jest.fn(),
|
||||
},
|
||||
spaces: () => null,
|
||||
} as any
|
||||
} as unknown) as AlertingPluginsStart
|
||||
);
|
||||
|
||||
const fakeRequest = {
|
||||
const fakeRequest = ({
|
||||
headers: {},
|
||||
getBasePath: () => '',
|
||||
path: '/',
|
||||
|
@ -141,8 +142,8 @@ describe('Alerting Plugin', () => {
|
|||
},
|
||||
},
|
||||
getSavedObjectsClient: jest.fn(),
|
||||
};
|
||||
await startContract.getAlertsClientWithRequest(fakeRequest as any);
|
||||
} as unknown) as KibanaRequest;
|
||||
await startContract.getAlertsClientWithRequest(fakeRequest);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,7 +117,10 @@ export class AlertingPlugin {
|
|||
.toPromise();
|
||||
}
|
||||
|
||||
public async setup(core: CoreSetup, plugins: AlertingPluginsSetup): Promise<PluginSetupContract> {
|
||||
public async setup(
|
||||
core: CoreSetup<AlertingPluginsStart, unknown>,
|
||||
plugins: AlertingPluginsSetup
|
||||
): Promise<PluginSetupContract> {
|
||||
this.licenseState = new LicenseState(plugins.licensing.license$);
|
||||
this.spaces = plugins.spaces?.spacesService;
|
||||
this.security = plugins.security;
|
||||
|
@ -157,7 +160,7 @@ export class AlertingPlugin {
|
|||
|
||||
const usageCollection = plugins.usageCollection;
|
||||
if (usageCollection) {
|
||||
core.getStartServices().then(async ([, startPlugins]: [CoreStart, any, any]) => {
|
||||
core.getStartServices().then(async ([, startPlugins]) => {
|
||||
registerAlertsUsageCollector(usageCollection, startPlugins.taskManager);
|
||||
|
||||
initializeAlertingTelemetry(
|
||||
|
@ -246,7 +249,7 @@ export class AlertingPlugin {
|
|||
}
|
||||
|
||||
private createRouteHandlerContext = (): IContextProvider<
|
||||
RequestHandler<any, any, any>,
|
||||
RequestHandler<unknown, unknown, unknown>,
|
||||
'alerting'
|
||||
> => {
|
||||
const { alertTypeRegistry, alertsClientFactory } = this;
|
||||
|
|
|
@ -4,16 +4,33 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server';
|
||||
import {
|
||||
RequestHandlerContext,
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
IClusterClient,
|
||||
} from 'kibana/server';
|
||||
import { identity } from 'lodash';
|
||||
import { httpServerMock } from '../../../../../src/core/server/mocks';
|
||||
import { alertsClientMock } from '../alerts_client.mock';
|
||||
import { alertsClientMock, AlertsClientMock } from '../alerts_client.mock';
|
||||
import { AlertType } from '../../common';
|
||||
import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks';
|
||||
|
||||
export function mockHandlerArguments(
|
||||
{ alertsClient, listTypes: listTypesRes = [], elasticsearch }: any,
|
||||
req: any,
|
||||
{
|
||||
alertsClient = alertsClientMock.create(),
|
||||
listTypes: listTypesRes = [],
|
||||
elasticsearch = elasticsearchServiceMock.createSetup(),
|
||||
}: {
|
||||
alertsClient?: AlertsClientMock;
|
||||
listTypes?: AlertType[];
|
||||
elasticsearch?: jest.Mocked<{
|
||||
adminClient: jest.Mocked<IClusterClient>;
|
||||
}>;
|
||||
},
|
||||
req: unknown,
|
||||
res?: Array<MethodKeysOf<KibanaResponseFactory>>
|
||||
): [RequestHandlerContext, KibanaRequest<any, any, any, any>, KibanaResponseFactory] {
|
||||
): [RequestHandlerContext, KibanaRequest<unknown, unknown, unknown>, KibanaResponseFactory] {
|
||||
const listTypes = jest.fn(() => listTypesRes);
|
||||
return [
|
||||
({
|
||||
|
@ -25,7 +42,7 @@ export function mockHandlerArguments(
|
|||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext,
|
||||
req as KibanaRequest<any, any, any, any>,
|
||||
req as KibanaRequest<unknown, unknown, unknown>,
|
||||
mockResponseFactory(res),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ describe('createAlertRoute', () => {
|
|||
|
||||
alertsClient.create.mockResolvedValueOnce(createResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(alertsClient, {});
|
||||
const [context, req, res] = mockHandlerArguments({ alertsClient }, {});
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
|
@ -163,7 +163,7 @@ describe('createAlertRoute', () => {
|
|||
|
||||
alertsClient.create.mockResolvedValueOnce(createResult);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(alertsClient, {});
|
||||
const [context, req, res] = mockHandlerArguments({ alertsClient }, {});
|
||||
|
||||
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
|
||||
|
||||
|
|
|
@ -54,9 +54,9 @@ export const createAlertRoute = (router: IRouter, licenseState: LicenseState) =>
|
|||
handleDisabledApiKeysError(
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, TypeOf<typeof bodySchema>, any>,
|
||||
req: KibanaRequest<unknown, unknown, TypeOf<typeof bodySchema>>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
|
||||
if (!context.alerting) {
|
||||
|
|
|
@ -74,9 +74,12 @@ describe('deleteAlertRoute', () => {
|
|||
|
||||
alertsClient.delete.mockResolvedValueOnce({});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(alertsClient, {
|
||||
params: { id: '1' },
|
||||
});
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ alertsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
}
|
||||
);
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
|
@ -97,9 +100,12 @@ describe('deleteAlertRoute', () => {
|
|||
|
||||
alertsClient.delete.mockResolvedValueOnce({});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(alertsClient, {
|
||||
id: '1',
|
||||
});
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ alertsClient },
|
||||
{
|
||||
id: '1',
|
||||
}
|
||||
);
|
||||
|
||||
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
|
||||
|
||||
|
|
|
@ -33,9 +33,9 @@ export const deleteAlertRoute = (router: IRouter, licenseState: LicenseState) =>
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -33,9 +33,9 @@ export const disableAlertRoute = (router: IRouter, licenseState: LicenseState) =
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -35,9 +35,9 @@ export const enableAlertRoute = (router: IRouter, licenseState: LicenseState) =>
|
|||
handleDisabledApiKeysError(
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -108,13 +108,16 @@ describe('findAlertRoute', () => {
|
|||
data: [],
|
||||
});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(alertsClient, {
|
||||
query: {
|
||||
per_page: 1,
|
||||
page: 1,
|
||||
default_search_operator: 'OR',
|
||||
},
|
||||
});
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ alertsClient },
|
||||
{
|
||||
query: {
|
||||
per_page: 1,
|
||||
page: 1,
|
||||
default_search_operator: 'OR',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ export const findAlertRoute = (router: IRouter, licenseState: LicenseState) => {
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, TypeOf<typeof querySchema>, any, any>,
|
||||
req: KibanaRequest<unknown, TypeOf<typeof querySchema>, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -99,7 +99,7 @@ describe('getAlertRoute', () => {
|
|||
alertsClient.get.mockResolvedValueOnce(mockedAlert);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
alertsClient,
|
||||
{ alertsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
},
|
||||
|
@ -126,7 +126,7 @@ describe('getAlertRoute', () => {
|
|||
alertsClient.get.mockResolvedValueOnce(mockedAlert);
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
alertsClient,
|
||||
{ alertsClient },
|
||||
{
|
||||
params: { id: '1' },
|
||||
},
|
||||
|
|
|
@ -33,9 +33,9 @@ export const getAlertRoute = (router: IRouter, licenseState: LicenseState) => {
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -33,9 +33,9 @@ export const getAlertStateRoute = (router: IRouter, licenseState: LicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -39,9 +39,9 @@ export function healthRoute(
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, any, any>,
|
||||
req: KibanaRequest<unknown, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
try {
|
||||
const {
|
||||
|
|
|
@ -47,6 +47,7 @@ describe('listAlertTypesRoute', () => {
|
|||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: [],
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -62,6 +63,7 @@ describe('listAlertTypesRoute', () => {
|
|||
"name": "Default",
|
||||
},
|
||||
],
|
||||
"actionVariables": Array [],
|
||||
"defaultActionGroupId": "default",
|
||||
"id": "1",
|
||||
"name": "name",
|
||||
|
@ -99,6 +101,14 @@ describe('listAlertTypesRoute', () => {
|
|||
id: '1',
|
||||
name: 'name',
|
||||
enabled: true,
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: [],
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -147,6 +157,7 @@ describe('listAlertTypesRoute', () => {
|
|||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: [],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -26,9 +26,9 @@ export const listAlertTypesRoute = (router: IRouter, licenseState: LicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, any, any>,
|
||||
req: KibanaRequest<unknown, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -33,9 +33,9 @@ export const muteAllAlertRoute = (router: IRouter, licenseState: LicenseState) =
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -34,9 +34,9 @@ export const muteAlertInstanceRoute = (router: IRouter, licenseState: LicenseSta
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -33,9 +33,9 @@ export const unmuteAllAlertRoute = (router: IRouter, licenseState: LicenseState)
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -34,9 +34,9 @@ export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: LicenseS
|
|||
},
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -56,9 +56,9 @@ export const updateAlertRoute = (router: IRouter, licenseState: LicenseState) =>
|
|||
handleDisabledApiKeysError(
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, TypeOf<typeof bodySchema>, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, TypeOf<typeof bodySchema>>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -35,9 +35,9 @@ export const updateApiKeyRoute = (router: IRouter, licenseState: LicenseState) =
|
|||
handleDisabledApiKeysError(
|
||||
router.handleLegacyErrors(async function(
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
|
||||
req: KibanaRequest<TypeOf<typeof paramSchema>, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> {
|
||||
): Promise<IKibanaResponse> {
|
||||
verifyApiAccess(licenseState);
|
||||
if (!context.alerting) {
|
||||
return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
|
||||
|
|
|
@ -7,10 +7,17 @@ import * as t from 'io-ts';
|
|||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server';
|
||||
import { SanitizedAlert, AlertTaskState, alertParamsSchema, alertStateSchema } from '../../common';
|
||||
import {
|
||||
SanitizedAlert,
|
||||
AlertTaskState,
|
||||
alertParamsSchema,
|
||||
alertStateSchema,
|
||||
AlertTaskParams,
|
||||
} from '../../common';
|
||||
|
||||
export interface AlertTaskInstance extends ConcreteTaskInstance {
|
||||
state: AlertTaskState;
|
||||
params: AlertTaskParams;
|
||||
}
|
||||
|
||||
const enumerateErrorFields = (e: t.Errors) =>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { getNextRunAt } from './get_next_run_at';
|
||||
|
||||
const mockedNow = new Date('2019-06-03T18:55:25.982Z');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).Date = class Date extends global.Date {
|
||||
static now() {
|
||||
return mockedNow.getTime();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { pick, mapValues, omit, without } from 'lodash';
|
||||
import { Logger, SavedObject } from '../../../../../src/core/server';
|
||||
import { Logger, SavedObject, KibanaRequest } from '../../../../../src/core/server';
|
||||
import { TaskRunnerContext } from './task_runner_factory';
|
||||
import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server';
|
||||
import { createExecutionHandler } from './create_execution_handler';
|
||||
|
@ -93,7 +93,7 @@ export class TaskRunner {
|
|||
},
|
||||
};
|
||||
|
||||
return this.context.getServices(fakeRequest);
|
||||
return this.context.getServices((fakeRequest as unknown) as KibanaRequest);
|
||||
}
|
||||
|
||||
private getExecutionHandler(
|
||||
|
@ -178,7 +178,7 @@ export class TaskRunner {
|
|||
};
|
||||
eventLogger.startTiming(event);
|
||||
|
||||
let updatedAlertTypeState: void | Record<string, any>;
|
||||
let updatedAlertTypeState: void | Record<string, unknown>;
|
||||
try {
|
||||
updatedAlertTypeState = await this.alertType.executor({
|
||||
alertId,
|
||||
|
|
|
@ -29,7 +29,7 @@ export function transformActionParams({
|
|||
actionParams,
|
||||
state,
|
||||
}: TransformActionParamsOptions): AlertActionParams {
|
||||
const result = cloneDeep(actionParams, (value: any) => {
|
||||
const result = cloneDeep(actionParams, (value: unknown) => {
|
||||
if (!isString(value)) return;
|
||||
|
||||
// when the list of variables we pass in here changes,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
interface Resolvable<T> {
|
||||
resolve: (arg?: T) => void;
|
||||
resolve: (arg: T) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,12 +13,10 @@ interface Resolvable<T> {
|
|||
* coordinating async tests.
|
||||
*/
|
||||
export function resolvable<T>(): Promise<T> & Resolvable<T> {
|
||||
let resolve: (arg?: T) => void;
|
||||
const result = new Promise<T>(r => {
|
||||
resolve = r;
|
||||
}) as any;
|
||||
|
||||
result.resolve = (arg: T) => resolve(arg);
|
||||
|
||||
return result;
|
||||
let resolve: (arg: T) => void;
|
||||
return Object.assign(new Promise<T>(r => (resolve = r)), {
|
||||
resolve(arg: T) {
|
||||
return setTimeout(() => resolve(arg), 0);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,15 +7,22 @@
|
|||
import { AlertInstance } from './alert_instance';
|
||||
import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
|
||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||
import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../src/core/server';
|
||||
import {
|
||||
SavedObjectAttributes,
|
||||
SavedObjectsClientContract,
|
||||
KibanaRequest,
|
||||
} from '../../../../src/core/server';
|
||||
import { Alert, AlertActionParams, ActionGroup } from '../common';
|
||||
import { AlertsClient } from './alerts_client';
|
||||
export * from '../common';
|
||||
|
||||
// This will have to remain `any` until we can extend Alert Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type State = Record<string, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Context = Record<string, any>;
|
||||
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
|
||||
export type GetServicesFunction = (request: any) => Services;
|
||||
export type GetServicesFunction = (request: KibanaRequest) => Services;
|
||||
export type GetBasePathFunction = (spaceId?: string) => string;
|
||||
export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
|
||||
|
||||
|
@ -29,6 +36,8 @@ declare module 'src/core/server' {
|
|||
}
|
||||
|
||||
export interface Services {
|
||||
// This will have to remain `any` until we can extend Alert Services with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callCluster(path: string, opts: any): Promise<any>;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
}
|
||||
|
@ -42,6 +51,8 @@ export interface AlertExecutorOptions {
|
|||
startedAt: Date;
|
||||
previousStartedAt: Date | null;
|
||||
services: AlertServices;
|
||||
// This will have to remain `any` until we can extend Alert Executors with generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: Record<string, any>;
|
||||
state: State;
|
||||
spaceId: string;
|
||||
|
@ -61,7 +72,7 @@ export interface AlertType {
|
|||
id: string;
|
||||
name: string;
|
||||
validate?: {
|
||||
params?: { validate: (object: any) => any };
|
||||
params?: { validate: (object: unknown) => AlertExecutorOptions['params'] };
|
||||
};
|
||||
actionGroups: ActionGroup[];
|
||||
defaultActionGroupId: ActionGroup['id'];
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { APICaller } from 'kibana/server';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
|
||||
const alertTypeMetric = {
|
||||
scripted_metric: {
|
||||
|
@ -246,6 +247,8 @@ export async function getTotalCountAggregations(callCluster: APICaller, kibanaIn
|
|||
return {
|
||||
count_total: totalAlertsCount,
|
||||
count_by_type: Object.keys(results.aggregations.byAlertTypeId.value.types).reduce(
|
||||
// ES DSL aggregations are returned as `any` by callCluster
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(obj: any, key: string) => ({
|
||||
...obj,
|
||||
[key.replace('.', '__')]: results.aggregations.byAlertTypeId.value.types[key],
|
||||
|
@ -284,7 +287,7 @@ export async function getTotalCountAggregations(callCluster: APICaller, kibanaIn
|
|||
}
|
||||
|
||||
export async function getTotalCountInUse(callCluster: APICaller, kibanaInex: string) {
|
||||
const searchResult = await callCluster('search', {
|
||||
const searchResult: SearchResponse<unknown> = await callCluster('search', {
|
||||
index: kibanaInex,
|
||||
rest_total_hits_as_int: true,
|
||||
body: {
|
||||
|
@ -305,6 +308,8 @@ export async function getTotalCountInUse(callCluster: APICaller, kibanaInex: str
|
|||
0
|
||||
),
|
||||
countByType: Object.keys(searchResult.aggregations.byAlertTypeId.value.types).reduce(
|
||||
// ES DSL aggregations are returned as `any` by callCluster
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(obj: any, key: string) => ({
|
||||
...obj,
|
||||
[key.replace('.', '__')]: searchResult.aggregations.byAlertTypeId.value.types[key],
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { ParamsSchema, Params } from './alert_type_params';
|
||||
import { runTests } from './lib/core_query_types.test';
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
|
||||
const DefaultParams: Writable<Partial<Params>> = {
|
||||
index: 'index-name',
|
||||
|
@ -21,6 +22,7 @@ const DefaultParams: Writable<Partial<Params>> = {
|
|||
describe('alertType Params validate()', () => {
|
||||
runTests(ParamsSchema, DefaultParams);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let params: any;
|
||||
beforeEach(() => {
|
||||
params = { ...DefaultParams };
|
||||
|
@ -64,7 +66,7 @@ describe('alertType Params validate()', () => {
|
|||
return () => validate();
|
||||
}
|
||||
|
||||
function validate(): any {
|
||||
function validate(): TypeOf<typeof ParamsSchema> {
|
||||
return ParamsSchema.validate(params);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -30,12 +30,12 @@ export const ParamsSchema = schema.object(
|
|||
const betweenComparators = new Set(['between', 'notBetween']);
|
||||
|
||||
// using direct type not allowed, circular reference, so body is typed to any
|
||||
function validateParams(anyParams: any): string | undefined {
|
||||
function validateParams(anyParams: unknown): string | undefined {
|
||||
// validate core query parts, return if it fails validation (returning string)
|
||||
const coreQueryValidated = validateCoreQueryBody(anyParams);
|
||||
if (coreQueryValidated) return coreQueryValidated;
|
||||
|
||||
const { thresholdComparator, threshold }: Params = anyParams;
|
||||
const { thresholdComparator, threshold }: Params = anyParams as Params;
|
||||
|
||||
if (betweenComparators.has(thresholdComparator) && threshold.length === 1) {
|
||||
return i18n.translate('xpack.alertingBuiltins.indexThreshold.invalidThreshold2ErrorMessage', {
|
||||
|
|
|
@ -19,7 +19,8 @@ const DefaultParams: Writable<Partial<CoreQueryParams>> = {
|
|||
timeWindowUnit: 'm',
|
||||
};
|
||||
|
||||
export function runTests(schema: ObjectType, defaultTypeParams: Record<string, any>): void {
|
||||
export function runTests(schema: ObjectType, defaultTypeParams: Record<string, unknown>): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let params: any;
|
||||
|
||||
describe('coreQueryTypes', () => {
|
||||
|
@ -186,7 +187,7 @@ export function runTests(schema: ObjectType, defaultTypeParams: Record<string, a
|
|||
return () => validate();
|
||||
}
|
||||
|
||||
function validate(): any {
|
||||
function validate(): unknown {
|
||||
return schema.validate(params);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue