diff --git a/x-pack/plugins/secops/server/graphql/index.ts b/x-pack/plugins/secops/server/graphql/index.ts index 8cf5394a91a6c..0afee6dfc0367 100644 --- a/x-pack/plugins/secops/server/graphql/index.ts +++ b/x-pack/plugins/secops/server/graphql/index.ts @@ -5,6 +5,54 @@ */ import { rootSchema } from '../../common/graphql/root/schema.gql'; +import sourceMock from '../graphql/sources/source.mock'; +import sourcesMock from '../graphql/sources/sources.mock'; +import { Logger } from '../utils/logger'; import { sourcesSchema } from './sources/schema.gql'; export const schemas = [rootSchema, sourcesSchema]; + +// The types from graphql-tools/src/mock.ts 'any' based. I add slightly +// stricter types here, but these should go away when graphql-tools using something +// other than "any" in the future for its types. +// https://github.com/apollographql/graphql-tools/blob/master/src/mock.ts#L406 +interface Context { + req: { + payload: { + operationName: string; + }; + }; +} + +export const createMocks = (logger: Logger) => ({ + Query: () => ({ + allSources: (root: unknown, args: unknown, context: Context) => { + logger.info('Mock allSources'); + const operationName = context.req.payload.operationName.toLowerCase(); + switch (operationName) { + case 'test': { + logger.info(`Using mock for test ${sourceMock}`); + return sourcesMock; + } + default: { + logger.error(`Could not find a mock for: ${operationName}`); + return []; + } + } + }, + source: (root: unknown, args: unknown, context: Context) => { + logger.info('Mock source'); + const operationName = context.req.payload.operationName.toLowerCase(); + switch (operationName) { + case 'test': { + logger.info(`Using mock for test ${sourceMock}`); + return sourceMock; + } + default: { + logger.error(`Could not find a mock for: ${operationName}`); + return {}; + } + } + }, + }), +}); diff --git a/x-pack/plugins/secops/server/graphql/sources/source.mock.ts b/x-pack/plugins/secops/server/graphql/sources/source.mock.ts new file mode 100644 index 0000000000000..dc8c749c2387f --- /dev/null +++ b/x-pack/plugins/secops/server/graphql/sources/source.mock.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +/* tslint:disable */ + +export default +{ + "id": "default", + "configuration": { + "fields": { + "host": "beat.hostname" + } + } +} diff --git a/x-pack/plugins/secops/server/graphql/sources/sources.mock.ts b/x-pack/plugins/secops/server/graphql/sources/sources.mock.ts new file mode 100644 index 0000000000000..4e9442b9a0bd9 --- /dev/null +++ b/x-pack/plugins/secops/server/graphql/sources/sources.mock.ts @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/* tslint:disable */ + +export default +[ + { + "id": "default", + "configuration": { + "fields": { + "container": "docker.container.name", + "host": "beat.hostname", + "message": [ + "message", + "@message" + ] + } + } + } +] diff --git a/x-pack/plugins/secops/server/init_server.ts b/x-pack/plugins/secops/server/init_server.ts index 98107857289d4..b3e6b3f55b613 100644 --- a/x-pack/plugins/secops/server/init_server.ts +++ b/x-pack/plugins/secops/server/init_server.ts @@ -4,16 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IResolvers, makeExecutableSchema } from 'graphql-tools'; +import { addMockFunctionsToSchema, IResolvers, makeExecutableSchema } from 'graphql-tools'; + import { schemas } from './graphql'; +import { createMocks } from './graphql'; import { createSourcesResolvers } from './graphql/sources'; import { AppBackendLibs } from './lib/types'; +import { Logger } from './utils/logger'; + +export interface Config { + mocking: boolean; + logger: Logger; +} -export const initServer = (libs: AppBackendLibs) => { +export const initServer = (libs: AppBackendLibs, config: Config) => { + const { logger, mocking } = config; const schema = makeExecutableSchema({ resolvers: [createSourcesResolvers(libs) as IResolvers], typeDefs: schemas, }); + if (mocking) { + const mocks = createMocks(logger); + addMockFunctionsToSchema({ mocks, schema }); + } libs.framework.registerGraphQLEndpoint('/api/secops/graphql', schema); }; diff --git a/x-pack/plugins/secops/server/kibana.index.test.ts b/x-pack/plugins/secops/server/kibana.index.test.ts new file mode 100644 index 0000000000000..41f2f3dae898b --- /dev/null +++ b/x-pack/plugins/secops/server/kibana.index.test.ts @@ -0,0 +1,25 @@ +/* + * 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 { amMocking } from './kibana.index'; + +describe('kibana.index', () => { + describe('#amMocking', () => { + afterEach(() => delete process.env.INGEST_MOCKS); + + test('should return true when process.ENV.mocking is set to a lower case string true', () => { + process.env.INGEST_MOCKS = 'true'; + expect(amMocking()).toEqual(true); + }); + test('should return false when process.ENV.mocking is not set', () => { + expect(amMocking()).toEqual(false); + }); + test('should return false when process.ENV.mocking is not set to a lower case string (since I am picky)', () => { + process.env.INGEST_MOCKS = 'TRUE'; + expect(amMocking()).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/secops/server/kibana.index.ts b/x-pack/plugins/secops/server/kibana.index.ts index 645640cad3378..4c26a8dfea833 100644 --- a/x-pack/plugins/secops/server/kibana.index.ts +++ b/x-pack/plugins/secops/server/kibana.index.ts @@ -6,16 +6,34 @@ import { Server } from 'hapi'; import JoiNamespace from 'joi'; + import { initServer } from './init_server'; import { compose } from './lib/compose/kibana'; +import { createLogger } from './utils/logger'; + +const APP_ID = 'secops'; export interface KbnServer extends Server { - usage: any; + usage: unknown; } +export const amMocking = (): boolean => process.env.INGEST_MOCKS === 'true'; + export const initServerWithKibana = (kbnServer: KbnServer) => { + const logger = createLogger(kbnServer); + logger.info('Plugin initializing'); + + const mocking = amMocking(); + if (mocking) { + logger.info( + `Mocks for ${APP_ID} is activated. No real ${APP_ID} data will be used, only mocks will be used.` + ); + } + const libs = compose(kbnServer); - initServer(libs); + initServer(libs, { mocking, logger }); + + logger.info('Plugin done initializing'); }; export const getConfigSchema = (Joi: typeof JoiNamespace) => { diff --git a/x-pack/plugins/secops/server/utils/logger.test.ts b/x-pack/plugins/secops/server/utils/logger.test.ts new file mode 100644 index 0000000000000..b530776a7ce72 --- /dev/null +++ b/x-pack/plugins/secops/server/utils/logger.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { createLogger } from './logger'; + +const APP_ID = 'secops'; +const createMockServer = () => ({ log: jest.fn() }); + +describe('logger', () => { + describe('#createLogger', () => { + test('should log out debug', () => { + const kbnServer = createMockServer(); + const logger = createLogger(kbnServer as any); + logger.debug('debug information'); + expect(kbnServer.log.mock.calls[0][0]).toEqual(['debug', APP_ID]); + expect(kbnServer.log.mock.calls[0][1]).toEqual('debug information'); + }); + + test('should log out info', () => { + const kbnServer = createMockServer(); + const logger = createLogger(kbnServer as any); + logger.info('info information'); + expect(kbnServer.log.mock.calls[0][0]).toEqual(['info', APP_ID]); + expect(kbnServer.log.mock.calls[0][1]).toEqual('info information'); + }); + + test('should log out warn', () => { + const kbnServer = createMockServer(); + const logger = createLogger(kbnServer as any); + logger.warn('warn information'); + expect(kbnServer.log.mock.calls[0][0]).toEqual(['warning', APP_ID]); + expect(kbnServer.log.mock.calls[0][1]).toEqual('warn information'); + }); + + test('should log out error', () => { + const kbnServer = createMockServer(); + const logger = createLogger(kbnServer as any); + logger.error('error information'); + expect(kbnServer.log.mock.calls[0][0]).toEqual(['error', APP_ID]); + expect(kbnServer.log.mock.calls[0][1]).toEqual('error information'); + }); + }); +}); diff --git a/x-pack/plugins/secops/server/utils/logger.ts b/x-pack/plugins/secops/server/utils/logger.ts new file mode 100644 index 0000000000000..69722147d17fe --- /dev/null +++ b/x-pack/plugins/secops/server/utils/logger.ts @@ -0,0 +1,23 @@ +/* + * 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 { Server } from 'hapi'; + +const LOGGING_TAGS = ['secops']; + +export interface Logger { + debug: (message: string) => void; + info: (message: string) => void; + warn: (message: string) => void; + error: (message: string) => void; +} + +export const createLogger = (kbnServer: Readonly): Readonly => ({ + debug: (message: string) => kbnServer.log(['debug', ...LOGGING_TAGS], message), + info: (message: string) => kbnServer.log(['info', ...LOGGING_TAGS], message), + warn: (message: string) => kbnServer.log(['warning', ...LOGGING_TAGS], message), + error: (message: string) => kbnServer.log(['error', ...LOGGING_TAGS], message), +});