mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
This reverts commit 4fabe6c441
.
This commit is contained in:
parent
6b26b9c8c6
commit
3f4c2c89ec
8 changed files with 78 additions and 436 deletions
|
@ -4,43 +4,25 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// @ts-ignore missing local definition
|
||||
export * from './datatable';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './autocomplete';
|
||||
export * from './constants';
|
||||
export * from './dataurl';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './errors';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './expression_form_handlers';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './fetch';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './find_in_object';
|
||||
export * from './fonts';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './get_colors_from_palette';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './get_field_type';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './get_legend_config';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './handlebars';
|
||||
export * from './hex_to_rgb';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './httpurl';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './latest_change';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './missing_asset';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './palettes';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './pivot_object_array';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './resolve_dataurl';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './unquote_string';
|
||||
// @ts-ignore missing local definition
|
||||
export * from './url';
|
|
@ -5,13 +5,22 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { summarizeWorkpads } from '../workpad_collector';
|
||||
// @ts-ignore Missing local definition
|
||||
import { handleResponse } from '../collector';
|
||||
import { workpads } from '../../../__tests__/fixtures/workpads';
|
||||
|
||||
const getMockResponse = (mocks = workpads) => ({
|
||||
hits: {
|
||||
hits: mocks.map(workpad => ({
|
||||
_source: {
|
||||
'canvas-workpad': workpad,
|
||||
},
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
describe('usage collector handle es response data', () => {
|
||||
it('should summarize workpads, pages, and elements', () => {
|
||||
const usage = summarizeWorkpads(workpads);
|
||||
const usage = handleResponse(getMockResponse());
|
||||
expect(usage).to.eql({
|
||||
workpads: {
|
||||
total: 6, // num workpad documents in .kibana index
|
||||
|
@ -54,7 +63,7 @@ describe('usage collector handle es response data', () => {
|
|||
});
|
||||
|
||||
it('should collect correctly if an expression has null as an argument (possible sub-expression)', () => {
|
||||
const mockWorkpads = [
|
||||
const mockEsResponse = getMockResponse([
|
||||
{
|
||||
name: 'Tweet Data Workpad 1',
|
||||
id: 'workpad-ae00567f-5510-4d68-b07f-6b1661948e03',
|
||||
|
@ -74,8 +83,8 @@ describe('usage collector handle es response data', () => {
|
|||
'@created': '2018-07-25T22:56:31.460Z',
|
||||
assets: {},
|
||||
},
|
||||
];
|
||||
const usage = summarizeWorkpads(mockWorkpads);
|
||||
]);
|
||||
const usage = handleResponse(mockEsResponse);
|
||||
expect(usage).to.eql({
|
||||
workpads: { total: 1 },
|
||||
pages: { total: 1, per_workpad: { avg: 1, min: 1, max: 1 } },
|
||||
|
@ -85,7 +94,7 @@ describe('usage collector handle es response data', () => {
|
|||
});
|
||||
|
||||
it('should fail gracefully if workpad has 0 pages (corrupted workpad)', () => {
|
||||
const mockWorkpadsCorrupted = [
|
||||
const mockEsResponseCorrupted = getMockResponse([
|
||||
{
|
||||
name: 'Tweet Data Workpad 2',
|
||||
id: 'workpad-ae00567f-5510-4d68-b07f-6b1661948e03',
|
||||
|
@ -97,8 +106,8 @@ describe('usage collector handle es response data', () => {
|
|||
'@created': '2018-07-25T22:56:31.460Z',
|
||||
assets: {},
|
||||
},
|
||||
];
|
||||
const usage = summarizeWorkpads(mockWorkpadsCorrupted);
|
||||
]);
|
||||
const usage = handleResponse(mockEsResponseCorrupted);
|
||||
expect(usage).to.eql({
|
||||
workpads: { total: 1 },
|
||||
pages: { total: 0, per_workpad: { avg: 0, min: 0, max: 0 } },
|
||||
|
@ -108,7 +117,7 @@ describe('usage collector handle es response data', () => {
|
|||
});
|
||||
|
||||
it('should fail gracefully in general', () => {
|
||||
const usage = summarizeWorkpads([]);
|
||||
expect(usage).to.eql({});
|
||||
const usage = handleResponse({ hits: { total: 0 } });
|
||||
expect(usage).to.eql(undefined);
|
||||
});
|
||||
});
|
|
@ -1,84 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { summarizeCustomElements, CustomElementDocument } from '../custom_element_collector';
|
||||
|
||||
function mockCustomElement(...nodeExpressions: string[]): CustomElementDocument {
|
||||
return {
|
||||
content: JSON.stringify({
|
||||
selectedNodes: nodeExpressions.map(expression => ({
|
||||
expression,
|
||||
})),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
describe('custom_element_collector.handleResponse', () => {
|
||||
describe('invalid responses', () => {
|
||||
it('returns nothing if no valid hits', () => {
|
||||
expect(summarizeCustomElements([])).to.eql({});
|
||||
});
|
||||
|
||||
it('returns nothing if no valid elements', () => {
|
||||
const customElements = [
|
||||
{
|
||||
content: 'invalid json',
|
||||
},
|
||||
];
|
||||
|
||||
expect(summarizeCustomElements(customElements)).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
it('counts total custom elements', () => {
|
||||
const elements = [mockCustomElement(''), mockCustomElement('')];
|
||||
|
||||
const data = summarizeCustomElements(elements);
|
||||
expect(data.custom_elements).to.not.be(null);
|
||||
|
||||
if (data.custom_elements) {
|
||||
expect(data.custom_elements.count).to.equal(elements.length);
|
||||
}
|
||||
});
|
||||
|
||||
it('reports all the functions used in custom elements', () => {
|
||||
const functions1 = ['a', 'b', 'c'];
|
||||
const functions2 = ['c', 'd', 'e', 'f'];
|
||||
const expectedFunctions = Array.from(new Set([...functions1, ...functions2]));
|
||||
|
||||
const elements = [mockCustomElement(functions1.join('|')), mockCustomElement(...functions2)];
|
||||
|
||||
const data = summarizeCustomElements(elements);
|
||||
expect(data.custom_elements).to.not.be(null);
|
||||
|
||||
if (data.custom_elements) {
|
||||
expect(data.custom_elements.functions_in_use).to.eql(expectedFunctions);
|
||||
}
|
||||
});
|
||||
|
||||
it('reports minimum, maximum, and avg elements in a custom element', () => {
|
||||
const functionsMin = ['a', 'b', 'c'];
|
||||
const functionsMax = ['d', 'e', 'f', 'g', 'h'];
|
||||
const functionsOther = ['i', 'j', 'k', 'l'];
|
||||
const avgFunctions = (functionsMin.length + functionsMax.length + functionsOther.length) / 3;
|
||||
|
||||
const elements = [
|
||||
mockCustomElement(...functionsMin),
|
||||
mockCustomElement(...functionsMax),
|
||||
mockCustomElement(...functionsOther),
|
||||
];
|
||||
|
||||
const result = summarizeCustomElements(elements);
|
||||
expect(result.custom_elements).to.not.be(null);
|
||||
|
||||
if (result.custom_elements) {
|
||||
expect(result.custom_elements.elements.max).to.equal(functionsMax.length);
|
||||
expect(result.custom_elements.elements.min).to.equal(functionsMin.length);
|
||||
expect(result.custom_elements.elements.avg).to.equal(avgFunctions);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -4,109 +4,67 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchParams } from 'elasticsearch';
|
||||
import { sum as arraySum, min as arrayMin, max as arrayMax, get } from 'lodash';
|
||||
// @ts-ignore Library missing definitions
|
||||
import { fromExpression } from '@kbn/interpreter/common';
|
||||
import { CANVAS_TYPE } from '../../common/lib/constants';
|
||||
import { AST, collectFns } from './collector_helpers';
|
||||
import { TelemetryCollector } from './collector';
|
||||
import { CANVAS_USAGE_TYPE, CANVAS_TYPE } from '../../common/lib/constants';
|
||||
|
||||
interface Element {
|
||||
expression: string;
|
||||
}
|
||||
/*
|
||||
* @param ast: an ast that includes functions to track
|
||||
* @param cb: callback to do something with a function that has been found
|
||||
*/
|
||||
const collectFns = (ast, cb) => {
|
||||
if (ast.type === 'expression') {
|
||||
ast.chain.forEach(({ function: cFunction, arguments: cArguments }) => {
|
||||
cb(cFunction);
|
||||
|
||||
interface Page {
|
||||
elements: Element[];
|
||||
}
|
||||
// recurse the argumetns and update the set along the way
|
||||
Object.keys(cArguments).forEach(argName => {
|
||||
cArguments[argName].forEach(subAst => {
|
||||
if (subAst != null) {
|
||||
collectFns(subAst, cb);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
interface Workpad {
|
||||
pages: Page[];
|
||||
[s: string]: any; // Only concerned with the pages here, but allow workpads to have any values
|
||||
}
|
||||
|
||||
interface WorkpadSearch {
|
||||
[CANVAS_TYPE]: Workpad;
|
||||
}
|
||||
|
||||
interface WorkpadTelemetry {
|
||||
workpads?: {
|
||||
total: number;
|
||||
};
|
||||
pages?: {
|
||||
total: number;
|
||||
per_workpad: {
|
||||
avg: number;
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
};
|
||||
elements?: {
|
||||
total: number;
|
||||
per_page: {
|
||||
avg: number;
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
};
|
||||
functions?: {
|
||||
total: number;
|
||||
in_use: string[];
|
||||
per_element: {
|
||||
avg: number;
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Gather statistic about the given workpads
|
||||
|
||||
@param workpadDocs a collection of workpad documents
|
||||
@returns Workpad Telemetry Data
|
||||
*/
|
||||
export function summarizeWorkpads(workpadDocs: Workpad[]): WorkpadTelemetry {
|
||||
const functionSet = new Set();
|
||||
|
||||
if (workpadDocs.length === 0) {
|
||||
return {};
|
||||
export function handleResponse({ hits }) {
|
||||
const workpadDocs = get(hits, 'hits', null);
|
||||
if (workpadDocs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const functionSet = new Set();
|
||||
|
||||
// make a summary of info about each workpad
|
||||
const workpadsInfo = workpadDocs.map(workpad => {
|
||||
let pages = { count: 0 };
|
||||
const workpadsInfo = workpadDocs.map(hit => {
|
||||
const workpad = hit._source[CANVAS_TYPE];
|
||||
|
||||
let pages;
|
||||
try {
|
||||
pages = { count: workpad.pages.length };
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.warn(err, workpad);
|
||||
}
|
||||
const elementCounts = workpad.pages.reduce<number[]>(
|
||||
const elementCounts = workpad.pages.reduce(
|
||||
(accum, page) => accum.concat(page.elements.length),
|
||||
[]
|
||||
);
|
||||
const functionCounts = workpad.pages.reduce<number[]>((accum, page) => {
|
||||
const functionCounts = workpad.pages.reduce((accum, page) => {
|
||||
return page.elements.map(element => {
|
||||
const ast: AST = fromExpression(element.expression);
|
||||
const ast = fromExpression(element.expression);
|
||||
collectFns(ast, cFunction => {
|
||||
functionSet.add(cFunction);
|
||||
});
|
||||
return ast.chain.length; // get the number of parts in the expression
|
||||
});
|
||||
}, []);
|
||||
|
||||
return { pages, elementCounts, functionCounts };
|
||||
});
|
||||
|
||||
// combine together info from across the workpads
|
||||
const combinedWorkpadsInfo = workpadsInfo.reduce<{
|
||||
pageMin: number;
|
||||
pageMax: number;
|
||||
pageCounts: number[];
|
||||
elementCounts: number[];
|
||||
functionCounts: number[];
|
||||
}>(
|
||||
const combinedWorkpadsInfo = workpadsInfo.reduce(
|
||||
(accum, pageInfo) => {
|
||||
const { pages, elementCounts, functionCounts } = pageInfo;
|
||||
|
||||
|
@ -174,27 +132,29 @@ export function summarizeWorkpads(workpadDocs: Workpad[]): WorkpadTelemetry {
|
|||
};
|
||||
}
|
||||
|
||||
const workpadCollector: TelemetryCollector = async function customElementCollector(
|
||||
server,
|
||||
callCluster
|
||||
) {
|
||||
const index = server.config().get<string>('kibana.index');
|
||||
const searchParams: SearchParams = {
|
||||
size: 10000, // elasticsearch index.max_result_window default value
|
||||
index,
|
||||
ignoreUnavailable: true,
|
||||
filterPath: ['hits.hits._source.canvas-workpad', '-hits.hits._source.canvas-workpad.assets'],
|
||||
body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } },
|
||||
};
|
||||
export function registerCanvasUsageCollector(server) {
|
||||
const index = server.config().get('kibana.index');
|
||||
const collector = server.usage.collectorSet.makeUsageCollector({
|
||||
type: CANVAS_USAGE_TYPE,
|
||||
isReady: () => true,
|
||||
fetch: async callCluster => {
|
||||
const searchParams = {
|
||||
size: 10000, // elasticsearch index.max_result_window default value
|
||||
index,
|
||||
ignoreUnavailable: true,
|
||||
filterPath: [
|
||||
'hits.hits._source.canvas-workpad',
|
||||
'-hits.hits._source.canvas-workpad.assets',
|
||||
],
|
||||
body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } },
|
||||
};
|
||||
|
||||
const esResponse = await callCluster<WorkpadSearch>('search', searchParams);
|
||||
const esResponse = await callCluster('search', searchParams);
|
||||
if (get(esResponse, 'hits.hits.length') > 0) {
|
||||
return handleResponse(esResponse);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (get(esResponse, 'hits.hits.length') > 0) {
|
||||
const workpads = esResponse.hits.hits.map(hit => hit._source[CANVAS_TYPE]);
|
||||
return summarizeWorkpads(workpads);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
export { workpadCollector };
|
||||
server.usage.collectorSet.register(collector);
|
||||
}
|
|
@ -1,54 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
|
||||
import { Legacy } from 'kibana';
|
||||
// @ts-ignore missing local declaration
|
||||
import { CANVAS_USAGE_TYPE } from '../../common/lib/constants';
|
||||
import { workpadCollector } from './workpad_collector';
|
||||
import { customElementCollector } from './custom_element_collector';
|
||||
|
||||
/**
|
||||
Function for collecting information about canvas usage
|
||||
*/
|
||||
export type TelemetryCollector = (
|
||||
/** The server instance */
|
||||
server: Legacy.Server,
|
||||
/** Function for calling elasticsearch */
|
||||
callCluster: CallCluster
|
||||
) => Record<string, any>;
|
||||
|
||||
const collectors: TelemetryCollector[] = [workpadCollector, customElementCollector];
|
||||
|
||||
/*
|
||||
Register the canvas usage collector function
|
||||
|
||||
This will call all of the defined collectors and combine the individual results into a single object
|
||||
to be returned to the caller.
|
||||
|
||||
A usage collector function returns an object derived from current data in the ES Cluster.
|
||||
*/
|
||||
export function registerCanvasUsageCollector(server: Legacy.Server) {
|
||||
const canvasCollector = server.usage.collectorSet.makeUsageCollector({
|
||||
type: CANVAS_USAGE_TYPE,
|
||||
isReady: () => true,
|
||||
fetch: async (callCluster: CallCluster) => {
|
||||
const collectorResults = await Promise.all(
|
||||
collectors.map(collector => collector(server, callCluster))
|
||||
);
|
||||
|
||||
return collectorResults.reduce(
|
||||
(reduction, usage) => {
|
||||
return { ...reduction, ...usage };
|
||||
},
|
||||
|
||||
{}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
server.usage.collectorSet.register(canvasCollector);
|
||||
}
|
|
@ -1,37 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @param ast: an ast that includes functions to track
|
||||
* @param cb: callback to do something with a function that has been found
|
||||
*/
|
||||
|
||||
export interface AST {
|
||||
type: string;
|
||||
chain: Array<{
|
||||
function: string;
|
||||
arguments: {
|
||||
[s: string]: AST[];
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export function collectFns(ast: AST, cb: (functionName: string) => void) {
|
||||
if (ast.type === 'expression') {
|
||||
ast.chain.forEach(({ function: cFunction, arguments: cArguments }) => {
|
||||
cb(cFunction);
|
||||
|
||||
// recurse the arguments and update the set along the way
|
||||
Object.keys(cArguments).forEach(argName => {
|
||||
cArguments[argName].forEach(subAst => {
|
||||
if (subAst != null) {
|
||||
collectFns(subAst, cb);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,134 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SearchParams } from 'elasticsearch';
|
||||
import { get } from 'lodash';
|
||||
// @ts-ignore Library missing definitions
|
||||
import { fromExpression } from '@kbn/interpreter/common';
|
||||
import { AST, collectFns } from './collector_helpers';
|
||||
import { TelemetryCollector } from './collector';
|
||||
|
||||
const CUSTOM_ELEMENT_TYPE = 'canvas-element';
|
||||
interface CustomElementSearch {
|
||||
[CUSTOM_ELEMENT_TYPE]: CustomElementDocument;
|
||||
}
|
||||
|
||||
export interface CustomElementDocument {
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface CustomElementTelemetry {
|
||||
custom_elements?: {
|
||||
count: number;
|
||||
elements: {
|
||||
min: number;
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
functions_in_use: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface CustomElement {
|
||||
selectedNodes: Array<{
|
||||
expression: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
function isCustomElement(maybeCustomElement: any): maybeCustomElement is CustomElement {
|
||||
return (
|
||||
maybeCustomElement !== null &&
|
||||
Array.isArray(maybeCustomElement.selectedNodes) &&
|
||||
maybeCustomElement.selectedNodes.every(
|
||||
(node: any) => node.expression && typeof node.expression === 'string'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function parseJsonOrNull(maybeJson: string) {
|
||||
try {
|
||||
return JSON.parse(maybeJson);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Calculate statistics about a collection of CustomElement Documents
|
||||
|
||||
@param customElements - Array of CustomElement documents
|
||||
@returns Statistics about how Custom Elements are being used
|
||||
*/
|
||||
export function summarizeCustomElements(
|
||||
customElements: CustomElementDocument[]
|
||||
): CustomElementTelemetry {
|
||||
const functionSet = new Set<string>();
|
||||
|
||||
const parsedContents: CustomElement[] = customElements
|
||||
.map(element => element.content)
|
||||
.map(parseJsonOrNull)
|
||||
.filter(isCustomElement);
|
||||
|
||||
if (parsedContents.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const elements = {
|
||||
min: Infinity,
|
||||
max: -Infinity,
|
||||
avg: 0,
|
||||
};
|
||||
|
||||
let totalElements = 0;
|
||||
|
||||
parsedContents.map(contents => {
|
||||
contents.selectedNodes.map(node => {
|
||||
const ast: AST = fromExpression(node.expression);
|
||||
collectFns(ast, (cFunction: string) => {
|
||||
functionSet.add(cFunction);
|
||||
});
|
||||
});
|
||||
elements.min = Math.min(elements.min, contents.selectedNodes.length);
|
||||
elements.max = Math.max(elements.max, contents.selectedNodes.length);
|
||||
totalElements += contents.selectedNodes.length;
|
||||
});
|
||||
|
||||
elements.avg = totalElements / parsedContents.length;
|
||||
|
||||
return {
|
||||
custom_elements: {
|
||||
elements,
|
||||
count: customElements.length,
|
||||
functions_in_use: Array.from(functionSet),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const customElementCollector: TelemetryCollector = async function customElementCollector(
|
||||
server,
|
||||
callCluster
|
||||
) {
|
||||
const index = server.config().get<string>('kibana.index');
|
||||
|
||||
const customElementParams: SearchParams = {
|
||||
size: 10000,
|
||||
index,
|
||||
ignoreUnavailable: true,
|
||||
filterPath: [`hits.hits._source.${CUSTOM_ELEMENT_TYPE}.content`],
|
||||
body: { query: { bool: { filter: { term: { type: CUSTOM_ELEMENT_TYPE } } } } },
|
||||
};
|
||||
|
||||
const esResponse = await callCluster<CustomElementSearch>('search', customElementParams);
|
||||
|
||||
if (get(esResponse, 'hits.hits.length') > 0) {
|
||||
const customElements = esResponse.hits.hits.map(hit => hit._source[CUSTOM_ELEMENT_TYPE]);
|
||||
return summarizeCustomElements(customElements);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
export { customElementCollector };
|
Loading…
Add table
Add a link
Reference in a new issue