diff --git a/x-pack/plugins/event_log/server/es/context.mock.ts b/x-pack/plugins/event_log/server/es/context.mock.ts index 6581cd689e43d..c15fee803fb71 100644 --- a/x-pack/plugins/event_log/server/es/context.mock.ts +++ b/x-pack/plugins/event_log/server/es/context.mock.ts @@ -19,6 +19,7 @@ const createContextMock = () => { initialize: jest.fn(), waitTillReady: jest.fn(), esAdapter: clusterClientAdapterMock.create(), + initialized: true, }; return mock; }; diff --git a/x-pack/plugins/event_log/server/es/context.test.ts b/x-pack/plugins/event_log/server/es/context.test.ts new file mode 100644 index 0000000000000..09fe676a5762e --- /dev/null +++ b/x-pack/plugins/event_log/server/es/context.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createEsContext } from './context'; +import { ClusterClient, Logger } from '../../../../../src/core/server'; +import { elasticsearchServiceMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; +jest.mock('../lib/../../../../package.json', () => ({ + version: '1.2.3', +})); +type EsClusterClient = Pick, 'callAsInternalUser' | 'asScoped'>; + +let logger: Logger; +let clusterClient: EsClusterClient; + +beforeEach(() => { + logger = loggingServiceMock.createLogger(); + clusterClient = elasticsearchServiceMock.createClusterClient(); +}); + +describe('createEsContext', () => { + test('should return is ready state as falsy if not initialized', () => { + const context = createEsContext({ + logger, + clusterClientPromise: Promise.resolve(clusterClient), + indexNameRoot: 'test0', + }); + + expect(context.initialized).toBeFalsy(); + + context.initialize(); + expect(context.initialized).toBeTruthy(); + }); + + test('should return esNames', () => { + const context = createEsContext({ + logger, + clusterClientPromise: Promise.resolve(clusterClient), + indexNameRoot: 'test-index', + }); + + const esNames = context.esNames; + expect(esNames).toStrictEqual({ + base: 'test-index', + alias: 'test-index-event-log-1.2.3', + ilmPolicy: 'test-index-event-log-policy', + indexPattern: 'test-index-event-log-*', + indexPatternWithVersion: 'test-index-event-log-1.2.3-*', + indexTemplate: 'test-index-event-log-1.2.3-template', + initialIndex: 'test-index-event-log-1.2.3-000001', + }); + }); + + test('should return exist false for esAdapter ilm policy, index template and alias before initialize', async () => { + const context = createEsContext({ + logger, + clusterClientPromise: Promise.resolve(clusterClient), + indexNameRoot: 'test1', + }); + clusterClient.callAsInternalUser.mockResolvedValue(false); + + const doesAliasExist = await context.esAdapter.doesAliasExist(context.esNames.alias); + expect(doesAliasExist).toBeFalsy(); + + const doesIndexTemplateExist = await context.esAdapter.doesIndexTemplateExist( + context.esNames.indexTemplate + ); + expect(doesIndexTemplateExist).toBeFalsy(); + }); + + test('should return exist true for esAdapter ilm policy, index template and alias after initialize', async () => { + const context = createEsContext({ + logger, + clusterClientPromise: Promise.resolve(clusterClient), + indexNameRoot: 'test2', + }); + clusterClient.callAsInternalUser.mockResolvedValue(true); + context.initialize(); + + const doesIlmPolicyExist = await context.esAdapter.doesIlmPolicyExist( + context.esNames.ilmPolicy + ); + expect(doesIlmPolicyExist).toBeTruthy(); + + const doesAliasExist = await context.esAdapter.doesAliasExist(context.esNames.alias); + expect(doesAliasExist).toBeTruthy(); + + const doesIndexTemplateExist = await context.esAdapter.doesIndexTemplateExist( + context.esNames.indexTemplate + ); + expect(doesIndexTemplateExist).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/event_log/server/es/context.ts b/x-pack/plugins/event_log/server/es/context.ts index b8e351367b695..0b3f22c6eecc0 100644 --- a/x-pack/plugins/event_log/server/es/context.ts +++ b/x-pack/plugins/event_log/server/es/context.ts @@ -19,6 +19,7 @@ export interface EsContext { esAdapter: IClusterClientAdapter; initialize(): void; waitTillReady(): Promise; + initialized: boolean; } export interface EsError { @@ -41,7 +42,7 @@ class EsContextImpl implements EsContext { public readonly esNames: EsNames; public esAdapter: IClusterClientAdapter; private readonly readySignal: ReadySignal; - private initialized: boolean; + public initialized: boolean; constructor(params: EsContextCtorParams) { this.logger = params.logger; diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts new file mode 100644 index 0000000000000..c5f3e65581df9 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, + Logger, + RouteValidationResultFactory, +} from 'kibana/server'; +import { IEventLogService, IEventLogger } from '../../../../../plugins/event_log/server'; +import { IValidatedEvent } from '../../../../../plugins/event_log/server/types'; + +export const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger: Logger) => { + router.post( + { + path: `/api/log_event_fixture/{id}/_log`, + validate: { + // removed validation as schema is currently broken in tests + // blocked by: https://github.com/elastic/kibana/issues/61652 + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + body: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { id } = req.params as { id: string }; + const event: IValidatedEvent = req.body; + logger.info(`test fixture: log event: ${id} ${JSON.stringify(event)}`); + try { + await context.core.savedObjects.client.get('event_log_test', id); + logger.info(`found existing saved object`); + } catch (ex) { + logger.info(`log event error: ${ex}`); + await context.core.savedObjects.client.create('event_log_test', {}, { id }); + logger.info(`created saved object`); + } + eventLogger.logEvent(event); + logger.info(`logged`); + return res.ok({}); + } + ); +}; + +export const registerProviderActionsRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.post( + { + path: '/api/log_event_fixture/{provider}/_registerProviderActions', + validate: { + body: value => ({ value }), + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + options: { authRequired: false }, + }, + (context, request, response) => { + const { provider } = request.params as { provider: string }; + const actions = request.body; + try { + logger.info( + `test register provider actions: ${provider}, actions: ${JSON.stringify(actions)}` + ); + + eventLogService.registerProviderActions(provider, actions); + logger.info(`registered`); + } catch (e) { + return response.badRequest({ body: e }); + } + return response.ok({ body: {} }); + } + ); +}; + +export const isProviderActionRegisteredRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/{provider}/{action}/_isProviderActionRegistered`, + validate: { + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { provider, action } = req.params as { provider: string; action: string }; + logger.info(`test provider actions is registered: ${provider} for action: ${action}`); + + return res.ok({ + body: { + isProviderActionRegistered: eventLogService.isProviderActionRegistered(provider, action), + }, + }); + } + ); +}; + +export const getProviderActionsRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/{provider}/getProviderActions`, + validate: { + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { provider } = req.params as { provider: string }; + + logger.info(`test if get all provider actions is registered`); + return res.ok({ + body: { actions: [...(eventLogService.getProviderActions().get(provider) ?? [])] }, + }); + } + ); +}; + +export const getLoggerRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/getEventLogger/{event}`, + validate: { + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { event } = req.params as { event: string }; + logger.info(`test get event logger for event: ${event}`); + + return res.ok({ + body: { eventLogger: eventLogService.getLogger({ event: { provider: event } }) }, + }); + } + ); +}; + +export const isIndexingEntriesRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/isIndexingEntries`, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + logger.info(`test if event logger is indexing entries`); + return res.ok({ body: { isIndexingEntries: eventLogService.isIndexingEntries() } }); + } + ); +}; + +export const isEventLogServiceEnabledRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/isEventLogServiceEnabled`, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + logger.info(`test if event logger is enabled`); + return res.ok({ body: { isEnabled: eventLogService.isEnabled() } }); + } + ); +}; + +export const isEventLogServiceLoggingEntriesRoute = ( + router: IRouter, + eventLogService: IEventLogService, + logger: Logger +) => { + router.get( + { + path: `/api/log_event_fixture/isEventLogServiceLoggingEntries`, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + logger.info(`test if event logger is logging entries`); + return res.ok({ body: { isLoggingEntries: eventLogService.isLoggingEntries() } }); + } + ); +}; diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts index 1b6db3a9a31b4..2ef932d19e9ee 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts @@ -4,24 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Plugin, CoreSetup, Logger, PluginInitializerContext } from 'kibana/server'; +import { IEventLogService, IEventLogClientService } from '../../../../../plugins/event_log/server'; import { - Plugin, - CoreSetup, - RequestHandlerContext, - KibanaRequest, - KibanaResponseFactory, - IKibanaResponse, - IRouter, - Logger, - PluginInitializerContext, - RouteValidationResultFactory, -} from 'kibana/server'; -import { - IEventLogService, - IEventLogClientService, - IEventLogger, -} from '../../../../../plugins/event_log/server'; -import { IValidatedEvent } from '../../../../../plugins/event_log/server/types'; + logEventRoute, + registerProviderActionsRoute, + isProviderActionRegisteredRoute, + getProviderActionsRoute, + getLoggerRoute, + isIndexingEntriesRoute, + isEventLogServiceLoggingEntriesRoute, + isEventLogServiceEnabledRoute, +} from './init_routes'; // this plugin's dependendencies export interface EventLogFixtureSetupDeps { @@ -57,42 +51,17 @@ export class EventLogFixturePlugin }); logEventRoute(router, eventLogger, this.logger); + + // event log service api routes + registerProviderActionsRoute(router, eventLog, this.logger); + isProviderActionRegisteredRoute(router, eventLog, this.logger); + getProviderActionsRoute(router, eventLog, this.logger); + getLoggerRoute(router, eventLog, this.logger); + isIndexingEntriesRoute(router, eventLog, this.logger); + isEventLogServiceLoggingEntriesRoute(router, eventLog, this.logger); + isEventLogServiceEnabledRoute(router, eventLog, this.logger); } public start() {} public stop() {} } - -const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger: Logger) => { - router.post( - { - path: `/api/log_event_fixture/{id}/_log`, - validate: { - // removed validation as schema is currently broken in tests - // blocked by: https://github.com/elastic/kibana/issues/61652 - params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), - body: (value: any, { ok }: RouteValidationResultFactory) => ok(value), - }, - }, - async function( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise> { - const { id } = req.params as { id: string }; - const event: IValidatedEvent = req.body; - logger.info(`test fixture: log event: ${id} ${JSON.stringify(event)}`); - try { - await context.core.savedObjects.client.get('event_log_test', id); - logger.info(`found existing saved object`); - } catch (ex) { - logger.info(`log event error: ${ex}`); - await context.core.savedObjects.client.create('event_log_test', {}, { id }); - logger.info(`created saved object`); - } - eventLogger.logEvent(event); - logger.info(`logged`); - return res.ok({}); - } - ); -}; diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index b055b22879bf9..2de395308ce74 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -3,9 +3,181 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect/expect.js'; +import { IEvent } from '../../../../plugins/event_log/server'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const es = getService('legacyEs'); + const supertest = getService('supertest'); + const log = getService('log'); + const config = getService('config'); + const retry = getService('retry'); -export default function() { describe('Event Log service API', () => { - it('should allow logging an event', async () => {}); + it('should check if it is enabled', async () => { + const configValue = config + .get('kbnTestServer.serverArgs') + .find((val: string) => val === '--xpack.eventLog.enabled=true'); + const result = await isEventLogServiceEnabled(); + expect(configValue).to.be.eql(`--xpack.eventLog.enabled=${result.body.isEnabled}`); + }); + + it('should check if logging entries is enabled', async () => { + const configValue = config + .get('kbnTestServer.serverArgs') + .find((val: string) => val === '--xpack.eventLog.logEntries=true'); + const result = await isEventLogServiceLoggingEntries(); + expect(configValue).to.be.eql(`--xpack.eventLog.logEntries=${result.body.isLoggingEntries}`); + }); + + it('should check if indexing entries is enabled', async () => { + const configValue = config + .get('kbnTestServer.serverArgs') + .find((val: string) => val === '--xpack.eventLog.indexEntries=true'); + const result = await isIndexingEntries(); + const exists = await es.indices.exists({ index: '.kibana-event-log-*' }); + expect(exists).to.be.eql(true); + expect(configValue).to.be.eql( + `--xpack.eventLog.indexEntries=${result.body.isIndexingEntries}` + ); + }); + + it('should be able to check if provider actions is registered', async () => { + const initResult = await isProviderActionRegistered('provider3', 'action1'); + + if (!initResult.body.isProviderActionRegistered) { + await registerProviderActions('provider3', ['action1']); + } + const result1 = await isProviderActionRegistered('provider3', 'action1'); + expect(result1.body.isProviderActionRegistered).to.be.eql(true); + + const result = await isProviderActionRegistered('provider3', 'action2'); + expect(result.body.isProviderActionRegistered).to.be.eql(false); + }); + + it('should return error message if provider is registered', async () => { + const initResult = await isProviderActionRegistered('duplication', 'action1'); + + if (!initResult.body.isProviderActionRegistered) { + await registerProviderActions('duplication', ['action1', 'action2']); + } + + const result = await registerProviderActions('duplication', ['action1', 'action2']); + expect(result.badRequest).to.be.eql(true); + }); + + it('should allow to register provider actions and return all provider actions', async () => { + const initResult = await isProviderActionRegistered('provider1', 'action1'); + + if (!initResult.body.isProviderActionRegistered) { + await registerProviderActions('provider1', ['action1', 'action2']); + } + + const providerActions = await getProviderActions('provider1'); + expect(providerActions.body.actions).to.be.eql(['action1', 'action2']); + }); + + it('should allow to get event logger event log service', async () => { + const initResult = await isProviderActionRegistered('provider2', 'action1'); + + if (!initResult.body.isProviderActionRegistered) { + await registerProviderActions('provider2', ['action1', 'action2']); + } + const eventLogger = await getEventLogger('provider2'); + expect(eventLogger.body.eventLogger.initialProperties).to.be.eql({ + event: { provider: 'provider2' }, + }); + }); + + it('should allow write an event to index document if indexing entries is enabled', async () => { + const initResult = await isProviderActionRegistered('provider4', 'action1'); + + if (!initResult.body.isProviderActionRegistered) { + await registerProviderActions('provider4', ['action1', 'action2']); + } + + const eventId = '1'; + const event: IEvent = { + event: { action: 'action1', provider: 'provider4' }, + kibana: { saved_objects: [{ type: 'event_log_test', id: eventId }] }, + }; + await logTestEvent(eventId, event); + + await retry.try(async () => { + const uri = `/api/event_log/event_log_test/${eventId}/_find`; + log.debug(`calling ${uri}`); + const result = await supertest + .get(uri) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(result.body.data.length).to.be.eql(1); + }); + }); }); + + async function registerProviderActions(provider: string, actions: string[]) { + log.debug(`registerProviderActions ${provider}`); + return await supertest + .post(`/api/log_event_fixture/${provider}/_registerProviderActions`) + .set('kbn-xsrf', 'xxx') + .send(actions); + } + + async function isProviderActionRegistered(provider: string, action: string) { + log.debug(`isProviderActionRegistered ${provider} for action ${action}`); + return await supertest + .get(`/api/log_event_fixture/${provider}/${action}/_isProviderActionRegistered`) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + async function getProviderActions(provider: string) { + log.debug(`getProviderActions ${provider}`); + return await supertest + .get(`/api/log_event_fixture/${provider}/getProviderActions`) + .set('kbn-xsrf', 'xxx') + .expect(200); + } + + async function getEventLogger(event: string) { + log.debug(`isProviderActionRegistered for event ${event}`); + return await supertest + .get(`/api/log_event_fixture/getEventLogger/${event}`) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + async function isIndexingEntries() { + log.debug(`isIndexingEntries`); + return await supertest + .get(`/api/log_event_fixture/isIndexingEntries`) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + async function isEventLogServiceEnabled() { + log.debug(`isEventLogServiceEnabled`); + return await supertest + .get(`/api/log_event_fixture/isEventLogServiceEnabled`) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + async function isEventLogServiceLoggingEntries() { + log.debug(`isEventLogServiceLoggingEntries`); + return await supertest + .get(`/api/log_event_fixture/isEventLogServiceLoggingEntries`) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + async function logTestEvent(id: string, event: IEvent) { + log.debug(`Logging Event for Saved Object ${id}`); + return await supertest + .post(`/api/log_event_fixture/${id}/_log`) + .set('kbn-xsrf', 'foo') + .send(event) + .expect(200); + } }