From 80306936c13f31a93e43f318e3f01654042f3ae2 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 27 Jan 2022 15:21:42 -0500 Subject: [PATCH] [Lists] Add an instance of `ExceptionListClient` with server extension points turned off to context object provided to callbacks (#123885) * Add an instance of ExceptionListClient with server extension points turned off to the `context` provided to callbacks * Unit test cases to validate context --- .../exception_list_client.test.ts | 26 +++++++++++++++++++ .../exception_lists/exception_list_client.ts | 17 ++++++++++++ .../extension_point_storage.mock.ts | 2 ++ .../extension_point_storage_client.test.ts | 4 +-- .../server/services/extension_points/types.ts | 8 ++++++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts index 255aa587666c7..3b6f2cb6ae4f2 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts @@ -12,6 +12,7 @@ import { createExtensionPointStorageMock, } from '../extension_points/extension_point_storage.mock'; import type { ExtensionPointCallbackDataArgument } from '../extension_points'; +import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { getCreateExceptionListItemOptionsMock, @@ -49,13 +50,16 @@ describe('exception_list_client', () => { describe('server extension points execution', () => { let extensionPointStorageContext: ExtensionPointStorageContextMock; let exceptionListClient: ExceptionListClient; + let kibanaRequest: ReturnType; beforeEach(() => { extensionPointStorageContext = createExtensionPointStorageMock(); + kibanaRequest = httpServerMock.createKibanaRequest(); }); it('should initialize class instance with `enableServerExtensionPoints` enabled by default', async () => { exceptionListClient = new ExceptionListClient({ + request: kibanaRequest, savedObjectsClient: getExceptionListSavedObjectClientMock(), serverExtensionsClient: extensionPointStorageContext.extensionPointStorage.getClient(), user: 'elastic', @@ -98,6 +102,7 @@ describe('exception_list_client', () => { describe('and server extension points are enabled', () => { beforeEach(() => { exceptionListClient = new ExceptionListClient({ + request: kibanaRequest, savedObjectsClient: getExceptionListSavedObjectClientMock(), serverExtensionsClient: extensionPointStorageContext.extensionPointStorage.getClient(), @@ -111,6 +116,15 @@ describe('exception_list_client', () => { expect(getExtensionPointCallback()).toHaveBeenCalled(); }); + it('should provide `context` object to extension point callbacks', async () => { + await callExceptionListClientMethod(); + + expect(getExtensionPointCallback().mock.calls[0][0].context).toEqual({ + exceptionListClient: expect.any(ExceptionListClient), + request: kibanaRequest, + }); + }); + it('should error if extension point callback throws an error', async () => { const error = new Error('foo'); const extensionCallback = getExtensionPointCallback(); @@ -157,6 +171,7 @@ describe('exception_list_client', () => { beforeEach(() => { exceptionListClient = new ExceptionListClient({ enableServerExtensionPoints: false, + request: kibanaRequest, savedObjectsClient: getExceptionListSavedObjectClientMock(), serverExtensionsClient: extensionPointStorageContext.extensionPointStorage.getClient(), @@ -305,6 +320,7 @@ describe('exception_list_client', () => { (methodName, callExceptionListClientMethod, getExtensionPointCallback) => { beforeEach(() => { exceptionListClient = new ExceptionListClient({ + request: kibanaRequest, savedObjectsClient: getExceptionListSavedObjectClientMock(), serverExtensionsClient: extensionPointStorageContext.extensionPointStorage.getClient(), user: 'elastic', @@ -317,6 +333,15 @@ describe('exception_list_client', () => { expect(getExtensionPointCallback()).toHaveBeenCalled(); }); + it('should provide `context` object to extension point callbacks', async () => { + await callExceptionListClientMethod(); + + expect(getExtensionPointCallback().mock.calls[0][0].context).toEqual({ + exceptionListClient: expect.any(ExceptionListClient), + request: kibanaRequest, + }); + }); + it('should error if extension point callback throws an error', async () => { const error = new Error('foo'); const extensionCallback = getExtensionPointCallback(); @@ -332,6 +357,7 @@ describe('exception_list_client', () => { beforeEach(() => { exceptionListClient = new ExceptionListClient({ enableServerExtensionPoints: false, + request: kibanaRequest, savedObjectsClient: getExceptionListSavedObjectClientMock(), serverExtensionsClient: extensionPointStorageContext.extensionPointStorage.getClient(), diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 62fa8524cd466..93f5077f021d5 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -103,7 +103,24 @@ export class ExceptionListClient { } private getServerExtensionCallbackContext(): ServerExtensionCallbackContext { + const { user, serverExtensionsClient, savedObjectsClient, request } = this; + let exceptionListClient: undefined | ExceptionListClient; + return { + // Lazy getter so that we only initialize a new instance of the class if needed + get exceptionListClient(): ExceptionListClient { + if (!exceptionListClient) { + exceptionListClient = new ExceptionListClient({ + enableServerExtensionPoints: false, + request, + savedObjectsClient, + serverExtensionsClient, + user, + }); + } + + return exceptionListClient; + }, request: this.request, }; } diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts index 2a63da494dc3b..47eeee057e072 100644 --- a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts +++ b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts @@ -8,6 +8,7 @@ import { MockedLogger, loggerMock } from '@kbn/logging/mocks'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; +import { ExceptionListClient } from '../exception_lists/exception_list_client'; import { ExtensionPointStorage } from './extension_point_storage'; import { @@ -122,6 +123,7 @@ export const createExtensionPointStorageMock = ( return { callbackContext: { + exceptionListClient: {} as unknown as ExceptionListClient, request: httpServerMock.createKibanaRequest(), }, exceptionPreCreate, diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts index 1e094f7258f86..567a83fd2eb35 100644 --- a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts +++ b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts @@ -138,9 +138,7 @@ describe('When using the ExtensionPointStorageClient', () => { if (extensionPointsMock.type === 'exceptionsListPreCreateItem') { expect(extensionPointsMock.callback).toHaveBeenCalledWith( expect.objectContaining({ - context: { - request: expect.any(Object), - }, + context: callbackContext, }) ); } diff --git a/x-pack/plugins/lists/server/services/extension_points/types.ts b/x-pack/plugins/lists/server/services/extension_points/types.ts index c8fdddc4b0969..dd94612a60295 100644 --- a/x-pack/plugins/lists/server/services/extension_points/types.ts +++ b/x-pack/plugins/lists/server/services/extension_points/types.ts @@ -19,6 +19,7 @@ import { UpdateExceptionListItemOptions, } from '../exception_lists/exception_list_client_types'; import { PromiseFromStreams } from '../exception_lists/import_exception_list_and_items'; +import type { ExceptionListClient } from '../exception_lists/exception_list_client'; /** * The `this` context provided to extension point's callback function @@ -30,6 +31,13 @@ export interface ServerExtensionCallbackContext { * is not triggered via one of the HTTP handlers */ request?: KibanaRequest; + + /** + * An `ExceptionListClient` instance that **DOES NOT** execute server extension point callbacks. + * This client should be used when needing to access Exception List content from within an Extension + * Point to avoid circular infinite loops + */ + exceptionListClient: ExceptionListClient; } export type ServerExtensionCallback = (args: {