Skip to content

Commit

Permalink
uses post and pre save hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
YulNaumenko committed Oct 4, 2024
1 parent 35e9442 commit 6f9504f
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 118 deletions.
182 changes: 134 additions & 48 deletions x-pack/plugins/actions/server/actions_client/actions_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
InMemoryConnector,
ActionTypeExecutorResult,
ConnectorTokenClientContract,
HookServices,
} from '../types';
import { PreconfiguredActionDisabledModificationError } from '../lib/errors/preconfigured_action_disabled_modification';
import { ExecuteOptions } from '../lib/action_executor';
Expand Down Expand Up @@ -263,18 +264,25 @@ export class ActionsClient {
})
);

if (actionType.preSaveEventHandler) {
const hookServices: HookServices = {
scopedClusterClient: this.context.scopedClusterClient,
};

if (actionType.preSaveHook) {
try {
await actionType.preSaveEventHandler({
await actionType.preSaveHook({
connectorId: id,
config,
secrets,
logger: this.context.logger,
scopedClusterClient: this.context.scopedClusterClient,
request: this.context.request,
services: hookServices,
isUpdate: false,
});
} catch (error) {
this.context.auditLogger?.log(
connectorAuditEvent({
action: ConnectorAuditAction.CREATE,
action: ConnectorAuditAction.UPDATE,
savedObject: { type: 'action', id },
error,
})
Expand All @@ -283,17 +291,51 @@ export class ActionsClient {
}
}

const result = await this.context.unsecuredSavedObjectsClient.create(
'action',
{
actionTypeId,
name,
isMissingSecrets: false,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
},
{ id }
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let result: any;
try {
result = await this.context.unsecuredSavedObjectsClient.create(
'action',
{
actionTypeId,
name,
isMissingSecrets: false,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
},
{ id }
);
} catch (err) {
result = err;
this.context.logger.error(`postSaveHook update error for`);
}

const wasSuccessful = !(result instanceof Error);
const label = `connectorId: "${id}"; type: ${actionTypeId}`;
const tags = ['post-save-hook', id];

if (actionType.postSaveHook) {
try {
await actionType.postSaveHook({
connectorId: id,
config,
secrets,
logger: this.context.logger,
request: this.context.request,
services: hookServices,
isUpdate: true,
wasSuccessful,
});
} catch (err) {
this.context.logger.error(`postSaveHook update error for ${label}: ${err.message}`, {
tags,
});
}
}

if (!wasSuccessful) {
throw result;
}

return {
id: result.id,
Expand Down Expand Up @@ -376,19 +418,25 @@ export class ActionsClient {
})
);

if (actionType.preSaveEventHandler) {
const hookServices: HookServices = {
scopedClusterClient: this.context.scopedClusterClient,
};

if (actionType.preSaveHook) {
try {
await actionType.preSaveEventHandler({
await actionType.preSaveHook({
connectorId: id,
config,
secrets,
logger: this.context.logger,
scopedClusterClient: this.context.scopedClusterClient,
request: this.context.request,
services: hookServices,
isUpdate: true,
});
} catch (error) {
this.context.auditLogger?.log(
connectorAuditEvent({
action: ConnectorAuditAction.UPDATE,
action: ConnectorAuditAction.CREATE,
savedObject: { type: 'action', id },
error,
})
Expand All @@ -397,26 +445,60 @@ export class ActionsClient {
}
}

const result = await this.context.unsecuredSavedObjectsClient.create<RawAction>(
'action',
{
...attributes,
actionTypeId,
name,
isMissingSecrets: false,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
},
omitBy(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let result: any;
try {
result = await this.context.unsecuredSavedObjectsClient.create<RawAction>(
'action',
{
id,
overwrite: true,
references,
version,
...attributes,
actionTypeId,
name,
isMissingSecrets: false,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
},
isUndefined
)
);
omitBy(
{
id,
overwrite: true,
references,
version,
},
isUndefined
)
);
} catch (err) {
result = err;
this.context.logger.error(`postSaveHook update error for`);
}

const wasSuccessful = !(result instanceof Error);
const label = `connectorId: "${id}"; type: ${actionTypeId}`;
const tags = ['post-save-hook', id];

if (actionType.postSaveHook) {
try {
await actionType.postSaveHook({
connectorId: id,
config,
secrets,
logger: this.context.logger,
request: this.context.request,
services: hookServices,
isUpdate: true,
wasSuccessful,
});
} catch (err) {
this.context.logger.error(`postSaveHook update error for ${label}: ${err.message}`, {
tags,
});
}
}

if (!wasSuccessful) {
throw result;
}

try {
await this.context.connectorTokenClient.deleteConnectorTokens({ connectorId: id });
Expand Down Expand Up @@ -712,26 +794,30 @@ export class ActionsClient {
'action',
id
);
const { actionTypeId, config } = attributes;
const { actionTypeId, config, secrets } = attributes;
const actionType = this.context.actionTypeRegistry.get(actionTypeId);
const result = await this.context.unsecuredSavedObjectsClient.delete('action', id);

if (actionType.postDeleteEventHandler) {
const hookServices: HookServices = {
scopedClusterClient: this.context.scopedClusterClient,
};

if (actionType.postDeleteHook) {
try {
await actionType.postDeleteEventHandler({
await actionType.postDeleteHook({
connectorId: id,
config,
secrets,
logger: this.context.logger,
scopedClusterClient: this.context.scopedClusterClient,
request: this.context.request,
services: hookServices,
});
} catch (error) {
this.context.auditLogger?.log(
connectorAuditEvent({
action: ConnectorAuditAction.UPDATE,
savedObject: { type: 'action', id },
error,
})
const tags = ['post-delete-hook', id];
this.context.logger.error(
`The post delete hook failed for for connector "${id}": ${error.message}`,
{ tags }
);
throw error;
}
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { ServiceParams } from './types';
describe('Registration', () => {
const renderedVariables = { body: '' };
const mockRenderParameterTemplates = jest.fn().mockReturnValue(renderedVariables);
const mockDeleteEventHandler = jest.fn();
const mockSaveEventHandler = jest.fn();
const mockPreSaveHook = jest.fn();
const mockPostSaveHook = jest.fn();
const mockPostDeleteHook = jest.fn();

const connector = {
id: '.test',
Expand Down Expand Up @@ -51,8 +52,9 @@ describe('Registration', () => {
actionTypeRegistry,
connector: {
...connector,
postDeleteEventHandler: mockDeleteEventHandler,
preSaveEventHandler: mockSaveEventHandler,
preSaveHook: mockPreSaveHook,
postSaveHook: mockPostSaveHook,
postDeleteHook: mockPostDeleteHook,
},
configurationUtilities: mockedActionsConfig,
logger,
Expand All @@ -68,8 +70,9 @@ describe('Registration', () => {
executor: expect.any(Function),
getService: expect.any(Function),
renderParameterTemplates: expect.any(Function),
postDeleteEventHandler: expect.any(Function),
preSaveEventHandler: expect.any(Function),
preSaveHook: expect.any(Function),
postSaveHook: expect.any(Function),
postDeleteHook: expect.any(Function),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const register = <Config extends ActionTypeConfig, Secrets extends Action
isSystemActionType: connector.isSystemActionType,
getService: connector.getService,
getKibanaPrivileges: connector.getKibanaPrivileges,
postDeleteEventHandler: connector.postDeleteEventHandler,
preSaveEventHandler: connector.preSaveEventHandler,
preSaveHook: connector.preSaveHook,
postSaveHook: connector.postSaveHook,
postDeleteHook: connector.postDeleteHook,
});
};
44 changes: 28 additions & 16 deletions x-pack/plugins/actions/server/sub_action_framework/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type { LicenseType } from '@kbn/licensing-plugin/common/types';

import type { Method, AxiosRequestConfig } from 'axios';
import { KibanaRequest } from '@kbn/core-http-server';
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
import type { ActionsConfigurationUtilities } from '../actions_config';
import type {
ActionTypeParams,
Expand All @@ -20,6 +19,7 @@ import type {
ValidatorType as ValidationSchema,
} from '../types';
import type { SubActionConnector } from './sub_action_connector';
import type { HookServices } from '../types';

export interface ServiceParams<Config, Secrets> {
/**
Expand Down Expand Up @@ -77,19 +77,34 @@ export type Validators<Config, Secrets> = Array<
ConfigValidator<Config> | SecretsValidator<Secrets>
>;

export interface PreSaveConnectorEventHandlerParams<Config, Secrets> {
config?: Config;
secrets?: Secrets;
export interface PreSaveConnectorHookParams<Config, Secrets> {
connectorId: string;
config: Config;
secrets: Secrets;
logger: Logger;
scopedClusterClient?: IScopedClusterClient;
isUpdate?: boolean;
request: KibanaRequest;
services: HookServices;
isUpdate: boolean;
}

export interface PostDeleteConnectorEventHandlerParams<Config, Secrets> {
config?: Config;
secrets?: Secrets;
export interface PostSaveConnectorHookParams<Config, Secrets> {
connectorId: string;
config: Config;
secrets: Secrets;
logger: Logger;
request: KibanaRequest;
services: HookServices;
isUpdate: boolean;
wasSuccessful: boolean;
}

export interface PostDeleteConnectorHookParams<Config, Secrets> {
connectorId: string;
config: Config;
secrets: Secrets;
logger: Logger;
scopedClusterClient?: IScopedClusterClient;
services: HookServices;
request: KibanaRequest;
}

export interface SubActionConnectorType<Config, Secrets> {
Expand All @@ -108,12 +123,9 @@ export interface SubActionConnectorType<Config, Secrets> {
getKibanaPrivileges?: (args?: {
params?: { subAction: string; subActionParams: Record<string, unknown> };
}) => string[];
preSaveEventHandler?: (
params: PreSaveConnectorEventHandlerParams<Config, Secrets>
) => Promise<void>;
postDeleteEventHandler?: (
params: PreSaveConnectorEventHandlerParams<Config, Secrets>
) => Promise<void>;
preSaveHook?: (params: PreSaveConnectorHookParams<Config, Secrets>) => Promise<void>;
postSaveHook?: (params: PostSaveConnectorHookParams<Config, Secrets>) => Promise<void>;
postDeleteHook?: (params: PostDeleteConnectorHookParams<Config, Secrets>) => Promise<void>;
}

export interface ExecutorParams extends ActionTypeParams {
Expand Down
Loading

0 comments on commit 6f9504f

Please sign in to comment.