diff --git a/scripts/saved_objs_info.js b/scripts/saved_objs_info.js new file mode 100644 index 000000000000..7bf0fd624420 --- /dev/null +++ b/scripts/saved_objs_info.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/setup_node_env'); +require('../test/common/services/saved_object_info').runSavedObjInfoSvc(); diff --git a/test/common/services/saved_object_info.ts b/test/common/services/saved_object_info.ts deleted file mode 100644 index 81871908724d..000000000000 --- a/test/common/services/saved_object_info.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { inspect } from 'util'; - -import type { estypes } from '@elastic/elasticsearch'; - -import { ToolingLog } from '@kbn/dev-utils'; -import { FtrService } from '../ftr_provider_context'; - -export class SavedObjectInfoService extends FtrService { - private readonly es = this.ctx.getService('es'); - - public async logSoTypes(log: ToolingLog, msg: string | null = null) { - const types = await this.getTypes(); - - log.debug( - `\n### Saved Object Types ${msg || 'Count: ' + types.length}\n${inspect(types, { - compact: false, - depth: 99, - breakLength: 80, - sorted: true, - })}` - ); - } - - public async getTypes(index = '.kibana') { - try { - const { body } = await this.es.search({ - index, - size: 0, - body: { - aggs: { - savedobjs: { - terms: { - field: 'type', - }, - }, - }, - }, - }); - - const agg = body.aggregations?.savedobjs as - | estypes.AggregationsTermsAggregate<{ key: string; doc_count: number }> - | undefined; - - if (!agg?.buckets) { - throw new Error( - `expected es to return buckets of saved object types: ${inspect(body, { depth: 100 })}` - ); - } - - return agg.buckets; - } catch (error) { - throw new Error(`Error while searching for saved object types: ${error}`); - } - } -} diff --git a/test/common/services/saved_object_info/index.ts b/test/common/services/saved_object_info/index.ts new file mode 100644 index 000000000000..a8e777e4e3bb --- /dev/null +++ b/test/common/services/saved_object_info/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { run } from '@kbn/dev-utils'; +import { pipe } from 'fp-ts/function'; +import { payload, noop, areValid, print, expectedFlags } from './utils'; +import { types } from './saved_object_info'; + +export { SavedObjectInfoService } from './saved_object_info'; + +export const runSavedObjInfoSvc = () => + run( + async ({ flags, log }) => { + const printWith = print(log); + + const getAndFormatAndPrint = async () => + pipe(await types(flags.esUrl as string)(), payload, printWith()); + + return areValid(flags) ? getAndFormatAndPrint() : noop(); + }, + { + description: ` + +Show information pertaining to the saved objects in the .kibana index + +Examples: + +See 'saved_objects_info_svc.md' + + `, + flags: expectedFlags(), + } + ); diff --git a/test/common/services/saved_object_info/saved_object_info.ts b/test/common/services/saved_object_info/saved_object_info.ts new file mode 100644 index 000000000000..2084ff8e9f4f --- /dev/null +++ b/test/common/services/saved_object_info/saved_object_info.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Client } from '@elastic/elasticsearch'; +import url from 'url'; +import { Either, fromNullable, chain, getOrElse, toError } from 'fp-ts/Either'; +import { flow, pipe } from 'fp-ts/function'; +import * as TE from 'fp-ts/lib/TaskEither'; +import * as T from 'fp-ts/lib/Task'; +import { ToolingLog } from '@kbn/dev-utils'; +import { FtrService } from '../../ftr_provider_context'; +import { print } from './utils'; + +const pluck = (key: string) => (obj: any): Either => + fromNullable(new Error(`Missing ${key}`))(obj[key]); + +const query = { + aggs: { + savedobjs: { + terms: { + field: 'type', + }, + }, + }, +}; + +export const types = (node: string) => async (index: string = '.kibana') => + await pipe( + TE.tryCatch( + async () => { + const { body } = await new Client({ node }).search({ + index, + size: 0, + body: query, + }); + return body; + }, + (reason: any) => toError(reason) + ), + TE.map((resp: any) => + flow( + pluck('aggregations'), + chain(pluck('savedobjs')), + chain(pluck('buckets')), + getOrElse((err: Error) => err.message) + )(resp) + ), + TE.fold((x) => T.of(`Error while searching for saved object types: ${x}`), T.of) + )(); + +export class SavedObjectInfoService extends FtrService { + private readonly config = this.ctx.getService('config'); + + public async logSoTypes(log: ToolingLog, msg: string | null = null) { + // @ts-ignore + pipe(await types(url.format(this.config.get('servers.elasticsearch'))), print(log)(msg)); + } +} diff --git a/test/common/services/saved_object_info/saved_objects_info_svc.md b/test/common/services/saved_object_info/saved_objects_info_svc.md new file mode 100644 index 000000000000..2d623129e290 --- /dev/null +++ b/test/common/services/saved_object_info/saved_objects_info_svc.md @@ -0,0 +1,35 @@ +# Saved Objects Info Svc w/ CLI + +## Used via the cli + +Run the cli +> the **--esUrl** arg is required; tells the svc which elastic search endpoint to use + +```shell + λ node scripts/saved_objs_info.js --esUrl http://elastic:changeme@localhost:9220 --soTypes +``` + +Result + +```shell + ### types: + + [ + { + doc_count: 5, + key: 'canvas-workpad-template' + }, + { + doc_count: 1, + key: 'apm-telemetry' + }, + { + doc_count: 1, + key: 'config' + }, + { + doc_count: 1, + key: 'space' + } + ] +``` diff --git a/test/common/services/saved_object_info/utils.ts b/test/common/services/saved_object_info/utils.ts new file mode 100644 index 000000000000..43ec56505174 --- /dev/null +++ b/test/common/services/saved_object_info/utils.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { inspect } from 'util'; +import { createFlagError, ToolingLog } from '@kbn/dev-utils'; + +export const format = (obj: unknown) => + inspect(obj, { + compact: false, + depth: 99, + breakLength: 80, + sorted: true, + }); + +export const noop = () => {}; + +export const areValid = (flags: any) => { + if (flags.esUrl === '') throw createFlagError('please provide a single --esUrl flag'); + return true; +}; + +// @ts-ignore +export const print = (log: ToolingLog) => (msg: string | null = null) => ({ xs, count }) => + log.success(`\n### Saved Object Types ${msg || 'Count: ' + count}\n${xs}`); + +export const expectedFlags = () => ({ + string: ['esUrl'], + boolean: ['soTypes'], + help: ` +--esUrl Required, tells the app which url to point to +--soTypes Not Required, tells the svc to show the types within the .kibana index + `, +}); + +export const payload = (xs: any) => ({ + xs: format(xs), + count: xs.length, +});