mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Telemetry] Telemetry tools check all makeUsageCollector
calls (#79840)
This commit is contained in:
parent
4dc6f3b788
commit
70549c2ffa
21 changed files with 740 additions and 159 deletions
|
@ -88,7 +88,7 @@ export function runTelemetryCheck() {
|
|||
task: (context) => new Listr(checkCompatibleTypesTask(context), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
enabled: (_) => !!ignoreStoredJson,
|
||||
enabled: (_) => fix || !ignoreStoredJson,
|
||||
title: 'Checking Matching collector.schema against stored json files',
|
||||
task: (context) =>
|
||||
new Listr(checkMatchingSchemasTask(context, !fix), { exitOnError: true }),
|
||||
|
@ -96,7 +96,10 @@ export function runTelemetryCheck() {
|
|||
{
|
||||
enabled: (_) => fix,
|
||||
skip: ({ roots }: TaskContext) => {
|
||||
return roots.every(({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length);
|
||||
const noDiffs = roots.every(
|
||||
({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length
|
||||
);
|
||||
return noDiffs && 'No changes needed.';
|
||||
},
|
||||
title: 'Generating new telemetry mappings',
|
||||
task: (context) => new Listr(generateSchemasTask(context), { exitOnError: true }),
|
||||
|
@ -104,7 +107,10 @@ export function runTelemetryCheck() {
|
|||
{
|
||||
enabled: (_) => fix,
|
||||
skip: ({ roots }: TaskContext) => {
|
||||
return roots.every(({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length);
|
||||
const noDiffs = roots.every(
|
||||
({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length
|
||||
);
|
||||
return noDiffs && 'No changes needed.';
|
||||
},
|
||||
title: 'Updating telemetry mapping files',
|
||||
task: (context) => new Listr(writeToFileTask(context), { exitOnError: true }),
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`parseUsageCollection throws when \`makeUsageCollector\` argument is a function call 1`] = `
|
||||
"Error extracting collector in src/fixtures/telemetry_collectors/externally_defined_usage_collector/index.ts
|
||||
Error: makeUsageCollector argument must be an object."
|
||||
`;
|
||||
|
||||
exports[`parseUsageCollection throws when mapping fields is not defined 1`] = `
|
||||
"Error extracting collector in src/fixtures/telemetry_collectors/unmapped_collector.ts
|
||||
Error: usageCollector.schema must be defined."
|
||||
|
|
|
@ -33,7 +33,10 @@ describe('parseTelemetryRC', () => {
|
|||
{
|
||||
root: configRoot,
|
||||
output: configRoot,
|
||||
exclude: [path.resolve(configRoot, './unmapped_collector.ts')],
|
||||
exclude: [
|
||||
path.resolve(configRoot, './unmapped_collector.ts'),
|
||||
path.resolve(configRoot, './externally_defined_usage_collector/index.ts'),
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -33,7 +33,7 @@ export function loadFixtureProgram(fixtureName: string) {
|
|||
'src',
|
||||
'fixtures',
|
||||
'telemetry_collectors',
|
||||
`${fixtureName}.ts`
|
||||
`${fixtureName}`
|
||||
);
|
||||
const tsConfig = ts.findConfigFile('./', ts.sys.fileExists, 'tsconfig.json');
|
||||
if (!tsConfig) {
|
||||
|
@ -52,49 +52,56 @@ describe('parseUsageCollection', () => {
|
|||
it.todo('throws when a function is returned from fetch');
|
||||
it.todo('throws when an object is not returned from fetch');
|
||||
|
||||
it('throws when `makeUsageCollector` argument is a function call', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram(
|
||||
'externally_defined_usage_collector/index.ts'
|
||||
);
|
||||
expect(() => [...parseUsageCollection(sourceFile, program)]).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it('throws when mapping fields is not defined', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('unmapped_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('unmapped_collector.ts');
|
||||
expect(() => [...parseUsageCollection(sourceFile, program)]).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it('parses root level defined collector', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('working_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('working_collector.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual([parsedWorkingCollector]);
|
||||
});
|
||||
|
||||
it('parses collector with schema defined as union of spreads', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('schema_defined_with_spreads_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('schema_defined_with_spreads_collector.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual([parsedSchemaDefinedWithSpreadsCollector]);
|
||||
});
|
||||
|
||||
it('parses nested collectors', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('nested_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('nested_collector.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual([parsedNestedCollector]);
|
||||
});
|
||||
|
||||
it('parses imported schema property', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('imported_schema');
|
||||
const { program, sourceFile } = loadFixtureProgram('imported_schema.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual(parsedImportedSchemaCollector);
|
||||
});
|
||||
|
||||
it('parses externally defined collectors', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('externally_defined_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('externally_defined_collector.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual(parsedExternallyDefinedCollector);
|
||||
});
|
||||
|
||||
it('parses imported Usage interface', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('imported_usage_interface');
|
||||
const { program, sourceFile } = loadFixtureProgram('imported_usage_interface.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual(parsedImportedUsageInterface);
|
||||
});
|
||||
|
||||
it('skips files that do not define a collector', () => {
|
||||
const { program, sourceFile } = loadFixtureProgram('file_with_no_collector');
|
||||
const { program, sourceFile } = loadFixtureProgram('file_with_no_collector.ts');
|
||||
const result = [...parseUsageCollection(sourceFile, program)];
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
|
|
@ -178,13 +178,11 @@ export function sourceHasUsageCollector(sourceFile: ts.SourceFile) {
|
|||
}
|
||||
|
||||
const identifiers = (sourceFile as any).identifiers;
|
||||
if (
|
||||
(!identifiers.get('makeUsageCollector') && !identifiers.get('type')) ||
|
||||
!identifiers.get('fetch')
|
||||
) {
|
||||
return false;
|
||||
if (identifiers.get('makeUsageCollector')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"root": ".",
|
||||
"output": ".",
|
||||
"exclude": [
|
||||
"./unmapped_collector.ts"
|
||||
"./unmapped_collector.ts",
|
||||
"./externally_defined_usage_collector/index.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export interface Usage {
|
||||
collectorName: string;
|
||||
}
|
||||
|
||||
export function getUsageCollector(collectorName: string) {
|
||||
return {
|
||||
type: 'externally_defined_usage_collector',
|
||||
isReady: () => true,
|
||||
schema: {
|
||||
collectorName: {
|
||||
type: 'keyword' as 'keyword',
|
||||
},
|
||||
},
|
||||
fetch(): Usage {
|
||||
return {
|
||||
collectorName,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { getUsageCollector } from './get_usage_collector';
|
||||
|
||||
export function registerCollector(collectorSet: UsageCollectionSetup) {
|
||||
const collectorName = 'some_configs';
|
||||
const collector = collectorSet.makeUsageCollector(getUsageCollector(collectorName));
|
||||
|
||||
collectorSet.registerCollector(collector);
|
||||
}
|
|
@ -1290,6 +1290,235 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"core": {
|
||||
"properties": {
|
||||
"config": {
|
||||
"properties": {
|
||||
"elasticsearch": {
|
||||
"properties": {
|
||||
"sniffOnStart": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sniffIntervalMs": {
|
||||
"type": "long"
|
||||
},
|
||||
"sniffOnConnectionFault": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"numberOfHostsConfigured": {
|
||||
"type": "long"
|
||||
},
|
||||
"requestHeadersWhitelistConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"customHeadersConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"shardTimeoutMs": {
|
||||
"type": "long"
|
||||
},
|
||||
"requestTimeoutMs": {
|
||||
"type": "long"
|
||||
},
|
||||
"pingTimeoutMs": {
|
||||
"type": "long"
|
||||
},
|
||||
"logQueries": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ssl": {
|
||||
"properties": {
|
||||
"verificationMode": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"certificateAuthoritiesConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"certificateConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keyConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keystoreConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"truststoreConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"alwaysPresentCertificate": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"healthCheckDelayMs": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"properties": {
|
||||
"basePathConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"maxPayloadInBytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"rewriteBasePath": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keepaliveTimeout": {
|
||||
"type": "long"
|
||||
},
|
||||
"socketTimeout": {
|
||||
"type": "long"
|
||||
},
|
||||
"compression": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"referrerWhitelistConfigured": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xsrf": {
|
||||
"properties": {
|
||||
"disableProtection": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"whitelistConfigured": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestId": {
|
||||
"properties": {
|
||||
"allowFromAnyIp": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ipAllowlistConfigured": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ssl": {
|
||||
"properties": {
|
||||
"certificateAuthoritiesConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"certificateConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cipherSuites": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"keyConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keystoreConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"truststoreConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"redirectHttpFromPortConfigured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"supportedProtocols": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"clientAuthentication": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"properties": {
|
||||
"appendersTypesUsed": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"loggersConfiguredCount": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedObjects": {
|
||||
"properties": {
|
||||
"maxImportPayloadBytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"maxImportExportSizeBytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"properties": {
|
||||
"memory": {
|
||||
"properties": {
|
||||
"heapSizeLimit": {
|
||||
"type": "long"
|
||||
},
|
||||
"heapTotalBytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"heapUsedBytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"properties": {
|
||||
"savedObjects": {
|
||||
"properties": {
|
||||
"indices": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"docsCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"docsDeleted": {
|
||||
"type": "long"
|
||||
},
|
||||
"alias": {
|
||||
"type": "text"
|
||||
},
|
||||
"primaryStoreSizeBytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"storeSizeBytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"csp": {
|
||||
"properties": {
|
||||
"strict": {
|
||||
|
@ -2502,6 +2731,48 @@
|
|||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vis_type_vega": {
|
||||
"properties": {
|
||||
"vega_lib_specs_total": {
|
||||
"type": "long"
|
||||
},
|
||||
"vega_lite_lib_specs_total": {
|
||||
"type": "long"
|
||||
},
|
||||
"vega_use_map_total": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization_types": {
|
||||
"properties": {
|
||||
"DYNAMIC_KEY": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "long"
|
||||
},
|
||||
"spaces_min": {
|
||||
"type": "long"
|
||||
},
|
||||
"spaces_max": {
|
||||
"type": "long"
|
||||
},
|
||||
"spaces_avg": {
|
||||
"type": "long"
|
||||
},
|
||||
"saved_7_days_total": {
|
||||
"type": "long"
|
||||
},
|
||||
"saved_30_days_total": {
|
||||
"type": "long"
|
||||
},
|
||||
"saved_90_days_total": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export const mockStats = { somestat: 1 };
|
||||
export const mockGetStats = jest.fn().mockResolvedValue(mockStats);
|
||||
jest.doMock('./get_usage_collector', () => ({
|
||||
getStats: mockGetStats,
|
||||
}));
|
|
@ -17,10 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { LegacyAPICaller } from 'src/core/server';
|
||||
import { getUsageCollector } from './get_usage_collector';
|
||||
import { getStats } from './get_usage_collector';
|
||||
import { HomeServerPluginSetup } from '../../../home/server';
|
||||
|
||||
const mockedSavedObjects = [
|
||||
|
@ -76,8 +74,8 @@ const getMockCallCluster = (hits?: unknown[]) =>
|
|||
jest.fn().mockReturnValue(Promise.resolve({ hits: { hits } }) as unknown) as LegacyAPICaller;
|
||||
|
||||
describe('Vega visualization usage collector', () => {
|
||||
const configMock = of({ kibana: { index: '' } });
|
||||
const usageCollector = getUsageCollector(configMock, {
|
||||
const mockIndex = 'mock_index';
|
||||
const mockDeps = {
|
||||
home: ({
|
||||
sampleData: {
|
||||
getSampleDatasets: jest.fn().mockReturnValue([
|
||||
|
@ -100,44 +98,37 @@ describe('Vega visualization usage collector', () => {
|
|||
]),
|
||||
},
|
||||
} as unknown) as HomeServerPluginSetup,
|
||||
});
|
||||
|
||||
test('Should fit the shape', () => {
|
||||
expect(usageCollector.type).toBe('vis_type_vega');
|
||||
expect(usageCollector.isReady()).toBe(true);
|
||||
expect(usageCollector.fetch).toEqual(expect.any(Function));
|
||||
});
|
||||
};
|
||||
|
||||
test('Returns undefined when no results found (undefined)', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster());
|
||||
const result = await getStats(getMockCallCluster(), mockIndex, mockDeps);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Returns undefined when no results found (0 results)', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster([]));
|
||||
const result = await getStats(getMockCallCluster([]), mockIndex, mockDeps);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Returns undefined when no vega saved objects found', async () => {
|
||||
const result = await usageCollector.fetch(
|
||||
getMockCallCluster([
|
||||
{
|
||||
_id: 'visualization:myvis-123',
|
||||
_source: {
|
||||
type: 'visualization',
|
||||
visualization: { visState: '{"type": "area"}' },
|
||||
},
|
||||
const mockCallCluster = getMockCallCluster([
|
||||
{
|
||||
_id: 'visualization:myvis-123',
|
||||
_source: {
|
||||
type: 'visualization',
|
||||
visualization: { visState: '{"type": "area"}' },
|
||||
},
|
||||
])
|
||||
);
|
||||
},
|
||||
]);
|
||||
const result = await getStats(mockCallCluster, mockIndex, mockDeps);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Should ingnore sample data visualizations', async () => {
|
||||
const callCluster = getMockCallCluster([
|
||||
const mockCallCluster = getMockCallCluster([
|
||||
{
|
||||
_id: 'visualization:sampledata-123',
|
||||
_source: {
|
||||
|
@ -155,13 +146,14 @@ describe('Vega visualization usage collector', () => {
|
|||
},
|
||||
]);
|
||||
|
||||
const result = await usageCollector.fetch(callCluster);
|
||||
const result = await getStats(mockCallCluster, mockIndex, mockDeps);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Summarizes visualizations response data', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster(mockedSavedObjects));
|
||||
const mockCallCluster = getMockCallCluster(mockedSavedObjects);
|
||||
const result = await getStats(mockCallCluster, mockIndex, mockDeps);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
vega_lib_specs_total: 2,
|
||||
|
|
|
@ -17,23 +17,16 @@
|
|||
* under the License.
|
||||
*/
|
||||
import { parse } from 'hjson';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { LegacyAPICaller, SavedObject } from 'src/core/server';
|
||||
|
||||
import {
|
||||
ConfigObservable,
|
||||
VegaSavedObjectAttributes,
|
||||
VisTypeVegaPluginSetupDependencies,
|
||||
} from '../types';
|
||||
import { VegaSavedObjectAttributes, VisTypeVegaPluginSetupDependencies } from '../types';
|
||||
|
||||
type UsageCollectorDependencies = Pick<VisTypeVegaPluginSetupDependencies, 'home'>;
|
||||
|
||||
type ESResponse = SearchResponse<{ visualization: { visState: string } }>;
|
||||
type VegaType = 'vega' | 'vega-lite';
|
||||
|
||||
const VEGA_USAGE_TYPE = 'vis_type_vega';
|
||||
|
||||
function isVegaType(attributes: any): attributes is VegaSavedObjectAttributes {
|
||||
return attributes && attributes.type === 'vega' && attributes.params?.spec;
|
||||
}
|
||||
|
@ -64,11 +57,25 @@ const getDefaultVegaVisualizations = (home: UsageCollectorDependencies['home'])
|
|||
return titles;
|
||||
};
|
||||
|
||||
const getStats = async (
|
||||
export interface VegaUsage {
|
||||
vega_lib_specs_total: number;
|
||||
vega_lite_lib_specs_total: number;
|
||||
vega_use_map_total: number;
|
||||
}
|
||||
|
||||
export const getStats = async (
|
||||
callCluster: LegacyAPICaller,
|
||||
index: string,
|
||||
{ home }: UsageCollectorDependencies
|
||||
) => {
|
||||
): Promise<VegaUsage | undefined> => {
|
||||
let shouldPublishTelemetry = false;
|
||||
|
||||
const vegaUsage = {
|
||||
vega_lib_specs_total: 0,
|
||||
vega_lite_lib_specs_total: 0,
|
||||
vega_use_map_total: 0,
|
||||
};
|
||||
|
||||
const searchParams = {
|
||||
size: 10000,
|
||||
index,
|
||||
|
@ -82,9 +89,9 @@ const getStats = async (
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
const esResponse: ESResponse = await callCluster('search', searchParams);
|
||||
const size = esResponse?.hits?.hits?.length ?? 0;
|
||||
let shouldPublishTelemetry = false;
|
||||
|
||||
if (!size) {
|
||||
return;
|
||||
|
@ -93,55 +100,32 @@ const getStats = async (
|
|||
// we want to exclude the Vega Sample Data visualizations from the stats
|
||||
// in order to have more accurate results
|
||||
const excludedFromStatsVisualizations = getDefaultVegaVisualizations(home);
|
||||
for (const hit of esResponse.hits.hits) {
|
||||
const visualization = hit._source?.visualization;
|
||||
const visState = JSON.parse(visualization?.visState ?? '{}');
|
||||
|
||||
const finalTelemetry = esResponse.hits.hits.reduce(
|
||||
(telemetry, hit) => {
|
||||
const visualization = hit._source?.visualization;
|
||||
const visState = JSON.parse(visualization?.visState ?? '{}');
|
||||
if (isVegaType(visState) && !excludedFromStatsVisualizations.includes(visState.title)) {
|
||||
try {
|
||||
const spec = parse(visState.params.spec, { legacyRoot: false });
|
||||
|
||||
if (isVegaType(visState) && !excludedFromStatsVisualizations.includes(visState.title))
|
||||
try {
|
||||
const spec = parse(visState.params.spec, { legacyRoot: false });
|
||||
if (spec) {
|
||||
shouldPublishTelemetry = true;
|
||||
|
||||
if (spec) {
|
||||
shouldPublishTelemetry = true;
|
||||
if (checkVegaSchemaType(spec.$schema, 'vega')) {
|
||||
telemetry.vega_lib_specs_total++;
|
||||
}
|
||||
if (checkVegaSchemaType(spec.$schema, 'vega-lite')) {
|
||||
telemetry.vega_lite_lib_specs_total++;
|
||||
}
|
||||
if (spec.config?.kibana?.type === 'map') {
|
||||
telemetry.vega_use_map_total++;
|
||||
}
|
||||
if (checkVegaSchemaType(spec.$schema, 'vega')) {
|
||||
vegaUsage.vega_lib_specs_total++;
|
||||
}
|
||||
if (checkVegaSchemaType(spec.$schema, 'vega-lite')) {
|
||||
vegaUsage.vega_lite_lib_specs_total++;
|
||||
}
|
||||
if (spec.config?.kibana?.type === 'map') {
|
||||
vegaUsage.vega_use_map_total++;
|
||||
}
|
||||
} catch (e) {
|
||||
// Let it go, the data is invalid and we'll don't need to handle it
|
||||
}
|
||||
|
||||
return telemetry;
|
||||
},
|
||||
{
|
||||
vega_lib_specs_total: 0,
|
||||
vega_lite_lib_specs_total: 0,
|
||||
vega_use_map_total: 0,
|
||||
} catch (e) {
|
||||
// Let it go, the data is invalid and we'll don't need to handle it
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return shouldPublishTelemetry ? finalTelemetry : undefined;
|
||||
return shouldPublishTelemetry ? vegaUsage : undefined;
|
||||
};
|
||||
|
||||
export function getUsageCollector(
|
||||
config: ConfigObservable,
|
||||
dependencies: UsageCollectorDependencies
|
||||
) {
|
||||
return {
|
||||
type: VEGA_USAGE_TYPE,
|
||||
isReady: () => true,
|
||||
fetch: async (callCluster: LegacyAPICaller) => {
|
||||
const { index } = (await config.pipe(first()).toPromise()).kibana;
|
||||
|
||||
return await getStats(callCluster, index, dependencies);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,16 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { getUsageCollector } from './get_usage_collector';
|
||||
import { ConfigObservable, VisTypeVegaPluginSetupDependencies } from '../types';
|
||||
|
||||
export function registerVegaUsageCollector(
|
||||
collectorSet: UsageCollectionSetup,
|
||||
config: ConfigObservable,
|
||||
dependencies: Pick<VisTypeVegaPluginSetupDependencies, 'home'>
|
||||
) {
|
||||
const collector = collectorSet.makeUsageCollector(getUsageCollector(config, dependencies));
|
||||
|
||||
collectorSet.registerCollector(collector);
|
||||
}
|
||||
export { registerVegaUsageCollector } from './register_vega_collector';
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { mockStats, mockGetStats } from './get_usage_collector.mock';
|
||||
import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock';
|
||||
import { HomeServerPluginSetup } from '../../../home/server';
|
||||
import { registerVegaUsageCollector } from './register_vega_collector';
|
||||
|
||||
describe('registerVegaUsageCollector', () => {
|
||||
const mockIndex = 'mock_index';
|
||||
const mockDeps = { home: ({} as unknown) as HomeServerPluginSetup };
|
||||
const mockConfig = of({ kibana: { index: mockIndex } });
|
||||
|
||||
it('makes a usage collector and registers it`', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps);
|
||||
expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1);
|
||||
expect(mockCollectorSet.registerCollector).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('makeUsageCollector configs fit the shape', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps);
|
||||
expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({
|
||||
type: 'vis_type_vega',
|
||||
isReady: expect.any(Function),
|
||||
fetch: expect.any(Function),
|
||||
schema: expect.any(Object),
|
||||
});
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
expect(usageCollectorConfig.isReady()).toBe(true);
|
||||
});
|
||||
|
||||
it('makeUsageCollector config.isReady returns true', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps);
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
expect(usageCollectorConfig.isReady()).toBe(true);
|
||||
});
|
||||
|
||||
it('makeUsageCollector config.fetch calls getStats', async () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps);
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
const mockCallCluster = jest.fn();
|
||||
const fetchResult = await usageCollectorConfig.fetch(mockCallCluster);
|
||||
expect(mockGetStats).toBeCalledTimes(1);
|
||||
expect(mockGetStats).toBeCalledWith(mockCallCluster, mockIndex, mockDeps);
|
||||
expect(fetchResult).toBe(mockStats);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { getStats, VegaUsage } from './get_usage_collector';
|
||||
import { ConfigObservable, VisTypeVegaPluginSetupDependencies } from '../types';
|
||||
|
||||
export function registerVegaUsageCollector(
|
||||
collectorSet: UsageCollectionSetup,
|
||||
config: ConfigObservable,
|
||||
dependencies: Pick<VisTypeVegaPluginSetupDependencies, 'home'>
|
||||
) {
|
||||
const collector = collectorSet.makeUsageCollector<VegaUsage | undefined>({
|
||||
type: 'vis_type_vega',
|
||||
isReady: () => true,
|
||||
schema: {
|
||||
vega_lib_specs_total: { type: 'long' },
|
||||
vega_lite_lib_specs_total: { type: 'long' },
|
||||
vega_use_map_total: { type: 'long' },
|
||||
},
|
||||
fetch: async (callCluster) => {
|
||||
const { index } = (await config.pipe(first()).toPromise()).kibana;
|
||||
|
||||
return await getStats(callCluster, index, dependencies);
|
||||
},
|
||||
});
|
||||
|
||||
collectorSet.registerCollector(collector);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export const mockStats = { somestat: 1 };
|
||||
export const mockGetStats = jest.fn().mockResolvedValue(mockStats);
|
||||
jest.doMock('./get_usage_collector', () => ({
|
||||
getStats: mockGetStats,
|
||||
}));
|
|
@ -18,10 +18,8 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { LegacyAPICaller } from 'src/core/server';
|
||||
import { getUsageCollector } from './get_usage_collector';
|
||||
import { getStats } from './get_usage_collector';
|
||||
|
||||
const defaultMockSavedObjects = [
|
||||
{
|
||||
|
@ -121,29 +119,22 @@ const enlargedMockSavedObjects = [
|
|||
];
|
||||
|
||||
describe('Visualizations usage collector', () => {
|
||||
const configMock = of({ kibana: { index: '' } });
|
||||
const usageCollector = getUsageCollector(configMock);
|
||||
const mockIndex = '';
|
||||
const getMockCallCluster = (hits: unknown[]) =>
|
||||
(() => Promise.resolve({ hits: { hits } }) as unknown) as LegacyAPICaller;
|
||||
|
||||
test('Should fit the shape', () => {
|
||||
expect(usageCollector.type).toBe('visualization_types');
|
||||
expect(usageCollector.isReady()).toBe(true);
|
||||
expect(usageCollector.fetch).toEqual(expect.any(Function));
|
||||
});
|
||||
|
||||
test('Returns undefined when no results found (undefined)', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster(undefined as any));
|
||||
expect(result).toBe(undefined);
|
||||
const result = await getStats(getMockCallCluster(undefined as any), mockIndex);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Returns undefined when no results found (0 results)', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster([]));
|
||||
expect(result).toBe(undefined);
|
||||
const result = await getStats(getMockCallCluster([]), mockIndex);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Summarizes visualizations response data', async () => {
|
||||
const result = await usageCollector.fetch(getMockCallCluster(defaultMockSavedObjects));
|
||||
const result = await getStats(getMockCallCluster(defaultMockSavedObjects), mockIndex);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
shell_beads: {
|
||||
|
@ -198,7 +189,7 @@ describe('Visualizations usage collector', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = await usageCollector.fetch(getMockCallCluster(enlargedMockSavedObjects));
|
||||
const result = await getStats(getMockCallCluster(enlargedMockSavedObjects), mockIndex);
|
||||
|
||||
expect(result).toMatchObject(expectedStats);
|
||||
});
|
||||
|
|
|
@ -17,16 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { countBy, get, groupBy, mapValues, max, min, values } from 'lodash';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
|
||||
import { LegacyAPICaller } from 'src/core/server';
|
||||
import { getPastDays } from './get_past_days';
|
||||
|
||||
const VIS_USAGE_TYPE = 'visualization_types';
|
||||
|
||||
type ESResponse = SearchResponse<{ visualization: { visState: string } }>;
|
||||
|
||||
interface VisSummary {
|
||||
|
@ -35,10 +31,25 @@ interface VisSummary {
|
|||
past_days: number;
|
||||
}
|
||||
|
||||
export interface VisualizationUsage {
|
||||
[x: string]: {
|
||||
total: number;
|
||||
spaces_min?: number;
|
||||
spaces_max?: number;
|
||||
spaces_avg: number;
|
||||
saved_7_days_total: number;
|
||||
saved_30_days_total: number;
|
||||
saved_90_days_total: number;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the response data into telemetry payload
|
||||
*/
|
||||
async function getStats(callCluster: LegacyAPICaller, index: string) {
|
||||
export async function getStats(
|
||||
callCluster: LegacyAPICaller,
|
||||
index: string
|
||||
): Promise<VisualizationUsage | undefined> {
|
||||
const searchParams = {
|
||||
size: 10000, // elasticsearch index.max_result_window default value
|
||||
index,
|
||||
|
@ -94,14 +105,3 @@ async function getStats(callCluster: LegacyAPICaller, index: string) {
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function getUsageCollector(config: Observable<{ kibana: { index: string } }>) {
|
||||
return {
|
||||
type: VIS_USAGE_TYPE,
|
||||
isReady: () => true,
|
||||
fetch: async (callCluster: LegacyAPICaller) => {
|
||||
const index = (await config.pipe(first()).toPromise()).kibana.index;
|
||||
return await getStats(callCluster, index);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,15 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { getUsageCollector } from './get_usage_collector';
|
||||
|
||||
export function registerVisualizationsCollector(
|
||||
collectorSet: UsageCollectionSetup,
|
||||
config: Observable<{ kibana: { index: string } }>
|
||||
) {
|
||||
const collector = collectorSet.makeUsageCollector(getUsageCollector(config));
|
||||
collectorSet.registerCollector(collector);
|
||||
}
|
||||
export { registerVisualizationsCollector } from './register_visualizations_collector';
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { mockStats, mockGetStats } from './get_usage_collector.mock';
|
||||
import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock';
|
||||
|
||||
import { registerVisualizationsCollector } from './register_visualizations_collector';
|
||||
|
||||
describe('registerVisualizationsCollector', () => {
|
||||
const mockIndex = 'mock_index';
|
||||
const mockConfig = of({ kibana: { index: mockIndex } });
|
||||
|
||||
it('makes a usage collector and registers it`', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVisualizationsCollector(mockCollectorSet, mockConfig);
|
||||
expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1);
|
||||
expect(mockCollectorSet.registerCollector).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('makeUsageCollector configs fit the shape', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVisualizationsCollector(mockCollectorSet, mockConfig);
|
||||
expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({
|
||||
type: 'visualization_types',
|
||||
isReady: expect.any(Function),
|
||||
fetch: expect.any(Function),
|
||||
schema: expect.any(Object),
|
||||
});
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
expect(usageCollectorConfig.isReady()).toBe(true);
|
||||
});
|
||||
|
||||
it('makeUsageCollector config.isReady returns true', () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVisualizationsCollector(mockCollectorSet, mockConfig);
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
expect(usageCollectorConfig.isReady()).toBe(true);
|
||||
});
|
||||
|
||||
it('makeUsageCollector config.fetch calls getStats', async () => {
|
||||
const mockCollectorSet = createUsageCollectionSetupMock();
|
||||
registerVisualizationsCollector(mockCollectorSet, mockConfig);
|
||||
const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
|
||||
const mockCallCluster = jest.fn();
|
||||
const fetchResult = await usageCollectorConfig.fetch(mockCallCluster);
|
||||
expect(mockGetStats).toBeCalledTimes(1);
|
||||
expect(mockGetStats).toBeCalledWith(mockCallCluster, mockIndex);
|
||||
expect(fetchResult).toBe(mockStats);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
|
||||
import { getStats, VisualizationUsage } from './get_usage_collector';
|
||||
|
||||
export function registerVisualizationsCollector(
|
||||
collectorSet: UsageCollectionSetup,
|
||||
config: Observable<{ kibana: { index: string } }>
|
||||
) {
|
||||
const collector = collectorSet.makeUsageCollector<VisualizationUsage | undefined>({
|
||||
type: 'visualization_types',
|
||||
isReady: () => true,
|
||||
schema: {
|
||||
DYNAMIC_KEY: {
|
||||
total: { type: 'long' },
|
||||
spaces_min: { type: 'long' },
|
||||
spaces_max: { type: 'long' },
|
||||
spaces_avg: { type: 'long' },
|
||||
saved_7_days_total: { type: 'long' },
|
||||
saved_30_days_total: { type: 'long' },
|
||||
saved_90_days_total: { type: 'long' },
|
||||
},
|
||||
},
|
||||
fetch: async (callCluster) => {
|
||||
const index = (await config.pipe(first()).toPromise()).kibana.index;
|
||||
return await getStats(callCluster, index);
|
||||
},
|
||||
});
|
||||
collectorSet.registerCollector(collector);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue