[Reporting/telemetry] Add 'statuses' object to usage to show status counts by jobType & appType (#63922)
* [Reporting] Additional status by app data for usage * --wip-- [skip ci] * clean up types * add a prettier-ignore * fix types * --wip-- [skip ci] * fix typo * more tests * Tweak the data model * fix the comments and type keys to reflect the data model Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
75fa843652
commit
6b5a96557f
343
x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap
generated
Normal file
343
x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap
generated
Normal file
|
@ -0,0 +1,343 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`data modeling with empty data 1`] = `
|
||||
Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"_all": 0,
|
||||
"available": true,
|
||||
"browser_type": undefined,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"_all": 0,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 0,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {},
|
||||
},
|
||||
"lastDay": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"_all": 0,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 0,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 0,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`data modeling with normal looking usage data 1`] = `
|
||||
Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 3,
|
||||
},
|
||||
"_all": 12,
|
||||
"available": true,
|
||||
"browser_type": undefined,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"_all": 1,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 0,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"completed_with_warnings": 1,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed_with_warnings": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"lastDay": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"_all": 1,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 0,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"completed_with_warnings": 1,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed_with_warnings": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": 6,
|
||||
"dashboard": 0,
|
||||
"visualization": 3,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 9,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 9,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 10,
|
||||
"completed_with_warnings": 1,
|
||||
"failed": 1,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed": Object {
|
||||
"PNG": Object {
|
||||
"visualization": 1,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": 6,
|
||||
"visualization": 3,
|
||||
},
|
||||
},
|
||||
"completed_with_warnings": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
"failed": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`data modeling with sparse data 1`] = `
|
||||
Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"_all": 4,
|
||||
"available": true,
|
||||
"browser_type": undefined,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"_all": 4,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 2,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 2,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 4,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
"csv": Object {},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"lastDay": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"_all": 4,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 2,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 2,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 4,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
"csv": Object {},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 2,
|
||||
"print": 0,
|
||||
},
|
||||
"total": 2,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 4,
|
||||
"failed": 0,
|
||||
},
|
||||
"statuses": Object {
|
||||
"completed": Object {
|
||||
"PNG": Object {
|
||||
"dashboard": 1,
|
||||
},
|
||||
"csv": Object {},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": 1,
|
||||
"dashboard": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { uniq } from 'lodash';
|
||||
import { CSV_JOB_TYPE, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants';
|
||||
import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types';
|
||||
import { AvailableTotal, ExportType, FeatureAvailabilityMap, RangeStats } from './types';
|
||||
|
||||
function getForFeature(
|
||||
range: Partial<RangeStats>,
|
||||
|
@ -47,6 +47,7 @@ export const decorateRangeStats = (
|
|||
const {
|
||||
_all: rangeAll,
|
||||
status: rangeStatus,
|
||||
statuses: rangeStatusByApp,
|
||||
[PDF_JOB_TYPE]: rangeStatsPdf,
|
||||
...rangeStatsBasic
|
||||
} = rangeStats;
|
||||
|
@ -73,6 +74,7 @@ export const decorateRangeStats = (
|
|||
const resultStats = {
|
||||
_all: rangeAll || 0,
|
||||
status: { completed: 0, failed: 0, ...rangeStatus },
|
||||
statuses: rangeStatusByApp,
|
||||
...rangePdf,
|
||||
...rangeBasic,
|
||||
} as RangeStats;
|
||||
|
|
|
@ -11,13 +11,13 @@ import { ReportingConfig } from '../types';
|
|||
import { decorateRangeStats } from './decorate_range_stats';
|
||||
import { getExportTypesHandler } from './get_export_type_handler';
|
||||
import {
|
||||
AggregationBuckets,
|
||||
AggregationResults,
|
||||
AggregationResultBuckets,
|
||||
FeatureAvailabilityMap,
|
||||
JobTypes,
|
||||
KeyCountBucket,
|
||||
RangeAggregationResults,
|
||||
RangeStats,
|
||||
SearchResponse,
|
||||
StatusByAppBucket,
|
||||
} from './types';
|
||||
|
||||
type XPackInfo = XPackMainPlugin['info'];
|
||||
|
@ -29,6 +29,7 @@ const LAYOUT_TYPES_FIELD = 'meta.layout.keyword';
|
|||
const OBJECT_TYPES_KEY = 'objectTypes';
|
||||
const OBJECT_TYPES_FIELD = 'meta.objectType.keyword';
|
||||
const STATUS_TYPES_KEY = 'statusTypes';
|
||||
const STATUS_BY_APP_KEY = 'statusByApp';
|
||||
const STATUS_TYPES_FIELD = 'status';
|
||||
|
||||
const DEFAULT_TERMS_SIZE = 10;
|
||||
|
@ -38,16 +39,30 @@ const PRINTABLE_PDF_JOBTYPE = 'printable_pdf';
|
|||
const getKeyCount = (buckets: KeyCountBucket[]): { [key: string]: number } =>
|
||||
buckets.reduce((accum, { key, doc_count: count }) => ({ ...accum, [key]: count }), {});
|
||||
|
||||
function getAggStats(aggs: AggregationResults) {
|
||||
const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY] as AggregationBuckets;
|
||||
const jobTypes: JobTypes = jobBuckets.reduce(
|
||||
(accum: JobTypes, { key, doc_count: count }: { key: string; doc_count: number }) => {
|
||||
// indexes some key/count buckets by statusType > jobType > appName: statusCount
|
||||
const getAppStatuses = (buckets: StatusByAppBucket[]) =>
|
||||
buckets.reduce((statuses, statusBucket) => {
|
||||
return {
|
||||
...accum,
|
||||
[key]: {
|
||||
total: count,
|
||||
},
|
||||
...statuses,
|
||||
[statusBucket.key]: statusBucket.jobTypes.buckets.reduce((jobTypes, job) => {
|
||||
return {
|
||||
...jobTypes,
|
||||
[job.key]: job.appNames.buckets.reduce((apps, app) => {
|
||||
return {
|
||||
...apps,
|
||||
[app.key]: app.doc_count,
|
||||
};
|
||||
}, {}),
|
||||
};
|
||||
}, {}),
|
||||
};
|
||||
}, {});
|
||||
|
||||
function getAggStats(aggs: AggregationResultBuckets): RangeStats {
|
||||
const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY];
|
||||
const jobTypes = jobBuckets.reduce(
|
||||
(accum: JobTypes, { key, doc_count: count }: { key: string; doc_count: number }) => {
|
||||
return { ...accum, [key]: { total: count } };
|
||||
},
|
||||
{} as JobTypes
|
||||
);
|
||||
|
@ -55,8 +70,8 @@ function getAggStats(aggs: AggregationResults) {
|
|||
// merge pdf stats into pdf jobtype key
|
||||
const pdfJobs = jobTypes[PRINTABLE_PDF_JOBTYPE];
|
||||
if (pdfJobs) {
|
||||
const pdfAppBuckets = get(aggs[OBJECT_TYPES_KEY], '.pdf.buckets', []);
|
||||
const pdfLayoutBuckets = get(aggs[LAYOUT_TYPES_KEY], '.pdf.buckets', []);
|
||||
const pdfAppBuckets = get<KeyCountBucket[]>(aggs[OBJECT_TYPES_KEY], '.pdf.buckets', []);
|
||||
const pdfLayoutBuckets = get<KeyCountBucket[]>(aggs[LAYOUT_TYPES_KEY], '.pdf.buckets', []);
|
||||
pdfJobs.app = getKeyCount(pdfAppBuckets) as {
|
||||
visualization: number;
|
||||
dashboard: number;
|
||||
|
@ -69,26 +84,35 @@ function getAggStats(aggs: AggregationResults) {
|
|||
|
||||
const all = aggs.doc_count as number;
|
||||
let statusTypes = {};
|
||||
const statusBuckets = get(aggs[STATUS_TYPES_KEY], 'buckets', []);
|
||||
const statusBuckets = get<KeyCountBucket[]>(aggs[STATUS_TYPES_KEY], 'buckets', []);
|
||||
if (statusBuckets) {
|
||||
statusTypes = getKeyCount(statusBuckets);
|
||||
}
|
||||
|
||||
return { _all: all, status: statusTypes, ...jobTypes };
|
||||
let statusByApp = {};
|
||||
const statusAppBuckets = get<StatusByAppBucket[]>(aggs[STATUS_BY_APP_KEY], 'buckets', []);
|
||||
if (statusAppBuckets) {
|
||||
statusByApp = getAppStatuses(statusAppBuckets);
|
||||
}
|
||||
|
||||
return { _all: all, status: statusTypes, statuses: statusByApp, ...jobTypes };
|
||||
}
|
||||
|
||||
type SearchAggregation = SearchResponse['aggregations']['ranges']['buckets'];
|
||||
|
||||
type RangeStatSets = Partial<
|
||||
RangeStats & {
|
||||
lastDay: RangeStats;
|
||||
last7Days: RangeStats;
|
||||
}
|
||||
>;
|
||||
async function handleResponse(response: AggregationResults): Promise<RangeStatSets> {
|
||||
const buckets = get(response, 'aggregations.ranges.buckets');
|
||||
|
||||
async function handleResponse(response: SearchResponse): Promise<RangeStatSets> {
|
||||
const buckets = get<SearchAggregation>(response, 'aggregations.ranges.buckets');
|
||||
if (!buckets) {
|
||||
return {};
|
||||
}
|
||||
const { lastDay, last7Days, all } = buckets as RangeAggregationResults;
|
||||
const { lastDay, last7Days, all } = buckets;
|
||||
|
||||
const lastDayUsage = lastDay ? getAggStats(lastDay) : ({} as RangeStats);
|
||||
const last7DaysUsage = last7Days ? getAggStats(last7Days) : ({} as RangeStats);
|
||||
|
@ -126,6 +150,17 @@ export async function getReportingUsage(
|
|||
aggs: {
|
||||
[JOB_TYPES_KEY]: { terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } },
|
||||
[STATUS_TYPES_KEY]: { terms: { field: STATUS_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } },
|
||||
[STATUS_BY_APP_KEY]: {
|
||||
terms: { field: 'status', size: DEFAULT_TERMS_SIZE },
|
||||
aggs: {
|
||||
jobTypes: {
|
||||
terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE },
|
||||
aggs: {
|
||||
appNames: { terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, // NOTE Discover/CSV export is missing the 'meta.objectType' field, so Discover/CSV results are missing for this agg
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[OBJECT_TYPES_KEY]: {
|
||||
filter: { term: { jobtype: PRINTABLE_PDF_JOBTYPE } },
|
||||
aggs: { pdf: { terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } } },
|
||||
|
@ -141,7 +176,7 @@ export async function getReportingUsage(
|
|||
};
|
||||
|
||||
return callCluster('search', params)
|
||||
.then((response: AggregationResults) => handleResponse(response))
|
||||
.then((response: SearchResponse) => handleResponse(response))
|
||||
.then((usage: RangeStatSets) => {
|
||||
// Allow this to explicitly throw an exception if/when this config is deprecated,
|
||||
// because we shouldn't collect browserType in that case!
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
getReportingUsageCollector,
|
||||
} from './reporting_usage_collector';
|
||||
import { ReportingConfig } from '../types';
|
||||
import { SearchResponse } from './types';
|
||||
|
||||
const exportTypesRegistry = getExportTypesRegistry();
|
||||
|
||||
|
@ -61,7 +62,7 @@ const getMockReportingConfig = () => ({
|
|||
get: () => {},
|
||||
kbnConfig: { get: () => '' },
|
||||
});
|
||||
const getResponseMock = (customization = {}) => customization;
|
||||
const getResponseMock = (base = {}) => base;
|
||||
|
||||
describe('license checks', () => {
|
||||
let mockConfig: ReportingConfig;
|
||||
|
@ -206,212 +207,143 @@ describe('data modeling', () => {
|
|||
ranges: {
|
||||
buckets: {
|
||||
all: {
|
||||
doc_count: 54,
|
||||
layoutTypes: {
|
||||
doc_count: 23,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'preserve_layout', doc_count: 13 },
|
||||
{ key: 'print', doc_count: 10 },
|
||||
],
|
||||
},
|
||||
},
|
||||
objectTypes: {
|
||||
doc_count: 23,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'dashboard', doc_count: 23 }],
|
||||
},
|
||||
},
|
||||
statusTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'pending', doc_count: 33 },
|
||||
{ key: 'completed', doc_count: 20 },
|
||||
{ key: 'processing', doc_count: 1 },
|
||||
],
|
||||
},
|
||||
jobTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'csv', doc_count: 27 },
|
||||
{ key: 'printable_pdf', doc_count: 23 },
|
||||
{ key: 'PNG', doc_count: 4 },
|
||||
],
|
||||
},
|
||||
},
|
||||
lastDay: {
|
||||
doc_count: 11,
|
||||
layoutTypes: {
|
||||
doc_count: 2,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'print', doc_count: 2 }],
|
||||
},
|
||||
},
|
||||
objectTypes: {
|
||||
doc_count: 2,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'dashboard', doc_count: 2 }],
|
||||
},
|
||||
},
|
||||
statusTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'pending', doc_count: 11 }],
|
||||
},
|
||||
jobTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'csv', doc_count: 5 },
|
||||
{ key: 'PNG', doc_count: 4 },
|
||||
{ key: 'printable_pdf', doc_count: 2 },
|
||||
],
|
||||
},
|
||||
doc_count: 12,
|
||||
jobTypes: { buckets: [ { doc_count: 9, key: 'printable_pdf' }, { doc_count: 3, key: 'PNG' }, ], },
|
||||
layoutTypes: { doc_count: 9, pdf: { buckets: [{ doc_count: 9, key: 'preserve_layout' }] }, },
|
||||
objectTypes: { doc_count: 9, pdf: { buckets: [ { doc_count: 6, key: 'canvas workpad' }, { doc_count: 3, key: 'visualization' }, ], }, },
|
||||
statusByApp: { buckets: [ { doc_count: 10, jobTypes: { buckets: [ { appNames: { buckets: [ { doc_count: 6, key: 'canvas workpad' }, { doc_count: 3, key: 'visualization' }, ], }, doc_count: 9, key: 'printable_pdf', }, { appNames: { buckets: [{ doc_count: 1, key: 'visualization' }] }, doc_count: 1, key: 'PNG', }, ], }, key: 'completed', }, { doc_count: 1, jobTypes: { buckets: [ { appNames: { buckets: [{ doc_count: 1, key: 'dashboard' }] }, doc_count: 1, key: 'PNG', }, ], }, key: 'completed_with_warnings', }, { doc_count: 1, jobTypes: { buckets: [ { appNames: { buckets: [{ doc_count: 1, key: 'dashboard' }] }, doc_count: 1, key: 'PNG', }, ], }, key: 'failed', }, ], },
|
||||
statusTypes: { buckets: [ { doc_count: 10, key: 'completed' }, { doc_count: 1, key: 'completed_with_warnings' }, { doc_count: 1, key: 'failed' }, ], },
|
||||
},
|
||||
last7Days: {
|
||||
doc_count: 27,
|
||||
layoutTypes: {
|
||||
doc_count: 13,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'print', doc_count: 10 },
|
||||
{ key: 'preserve_layout', doc_count: 3 },
|
||||
],
|
||||
doc_count: 1,
|
||||
jobTypes: { buckets: [{ doc_count: 1, key: 'PNG' }] },
|
||||
layoutTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
objectTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
statusByApp: { buckets: [ { doc_count: 1, jobTypes: { buckets: [ { appNames: { buckets: [{ doc_count: 1, key: 'dashboard' }] }, doc_count: 1, key: 'PNG', }, ], }, key: 'completed_with_warnings', }, ], },
|
||||
statusTypes: { buckets: [{ doc_count: 1, key: 'completed_with_warnings' }] },
|
||||
},
|
||||
},
|
||||
objectTypes: {
|
||||
doc_count: 13,
|
||||
pdf: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'dashboard', doc_count: 13 }],
|
||||
},
|
||||
},
|
||||
statusTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [{ key: 'pending', doc_count: 27 }],
|
||||
},
|
||||
jobTypes: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{ key: 'printable_pdf', doc_count: 13 },
|
||||
{ key: 'csv', doc_count: 10 },
|
||||
{ key: 'PNG', doc_count: 4 },
|
||||
],
|
||||
lastDay: {
|
||||
doc_count: 1,
|
||||
jobTypes: { buckets: [{ doc_count: 1, key: 'PNG' }] },
|
||||
layoutTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
objectTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
statusByApp: { buckets: [ { doc_count: 1, jobTypes: { buckets: [ { appNames: { buckets: [{ doc_count: 1, key: 'dashboard' }] }, doc_count: 1, key: 'PNG', }, ], }, key: 'completed_with_warnings', }, ], },
|
||||
statusTypes: { buckets: [{ doc_count: 1, key: 'completed_with_warnings' }] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} as SearchResponse) // prettier-ignore
|
||||
)
|
||||
);
|
||||
|
||||
const usageStats = await fetch(callClusterMock as any);
|
||||
expect(usageStats).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 4,
|
||||
},
|
||||
"_all": 54,
|
||||
"available": true,
|
||||
"browser_type": undefined,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 27,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 4,
|
||||
},
|
||||
"_all": 27,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 10,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 13,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 3,
|
||||
"print": 10,
|
||||
},
|
||||
"total": 13,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
"pending": 27,
|
||||
},
|
||||
},
|
||||
"lastDay": Object {
|
||||
"PNG": Object {
|
||||
"available": true,
|
||||
"total": 4,
|
||||
},
|
||||
"_all": 11,
|
||||
"csv": Object {
|
||||
"available": true,
|
||||
"total": 5,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 2,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 0,
|
||||
"print": 2,
|
||||
},
|
||||
"total": 2,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 0,
|
||||
"failed": 0,
|
||||
"pending": 11,
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 23,
|
||||
"visualization": 0,
|
||||
},
|
||||
"available": true,
|
||||
"layout": Object {
|
||||
"preserve_layout": 13,
|
||||
"print": 10,
|
||||
},
|
||||
"total": 23,
|
||||
},
|
||||
"status": Object {
|
||||
"completed": 20,
|
||||
"failed": 0,
|
||||
"pending": 33,
|
||||
"processing": 1,
|
||||
},
|
||||
expect(usageStats).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('with sparse data', async () => {
|
||||
const mockConfig = getMockReportingConfig();
|
||||
const plugins = getPluginsMock();
|
||||
const { fetch } = getReportingUsageCollector(
|
||||
mockConfig,
|
||||
plugins.usageCollection,
|
||||
plugins.__LEGACY.plugins.xpack_main.info,
|
||||
exportTypesRegistry,
|
||||
function isReady() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
`);
|
||||
);
|
||||
const callClusterMock = jest.fn(() =>
|
||||
Promise.resolve(
|
||||
getResponseMock({
|
||||
aggregations: {
|
||||
ranges: {
|
||||
buckets: {
|
||||
all: {
|
||||
doc_count: 4,
|
||||
layoutTypes: { doc_count: 2, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 2 }] }, },
|
||||
statusByApp: { buckets: [ { key: 'completed', doc_count: 4, jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2, appNames: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, }, { key: 'PNG', doc_count: 1, appNames: { buckets: [{ key: 'dashboard', doc_count: 1 }] }, }, { key: 'csv', doc_count: 1, appNames: { buckets: [] } }, ], }, }, ], },
|
||||
objectTypes: { doc_count: 2, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, },
|
||||
statusTypes: { buckets: [{ key: 'completed', doc_count: 4 }] },
|
||||
jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2 }, { key: 'PNG', doc_count: 1 }, { key: 'csv', doc_count: 1 }, ], },
|
||||
},
|
||||
last7Days: {
|
||||
doc_count: 4,
|
||||
layoutTypes: { doc_count: 2, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 2 }] }, },
|
||||
statusByApp: { buckets: [ { key: 'completed', doc_count: 4, jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2, appNames: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, }, { key: 'PNG', doc_count: 1, appNames: { buckets: [{ key: 'dashboard', doc_count: 1 }] }, }, { key: 'csv', doc_count: 1, appNames: { buckets: [] } }, ], }, }, ], },
|
||||
objectTypes: { doc_count: 2, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, },
|
||||
statusTypes: { buckets: [{ key: 'completed', doc_count: 4 }] },
|
||||
jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2 }, { key: 'PNG', doc_count: 1 }, { key: 'csv', doc_count: 1 }, ], },
|
||||
},
|
||||
lastDay: {
|
||||
doc_count: 4,
|
||||
layoutTypes: { doc_count: 2, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 2 }] }, },
|
||||
statusByApp: { buckets: [ { key: 'completed', doc_count: 4, jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2, appNames: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, }, { key: 'PNG', doc_count: 1, appNames: { buckets: [{ key: 'dashboard', doc_count: 1 }] }, }, { key: 'csv', doc_count: 1, appNames: { buckets: [] } }, ], }, }, ], },
|
||||
objectTypes: { doc_count: 2, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 1 }, { key: 'dashboard', doc_count: 1 }, ], }, },
|
||||
statusTypes: { buckets: [{ key: 'completed', doc_count: 4 }] },
|
||||
jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 2 }, { key: 'PNG', doc_count: 1 }, { key: 'csv', doc_count: 1 }, ], },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as SearchResponse) // prettier-ignore
|
||||
)
|
||||
);
|
||||
|
||||
const usageStats = await fetch(callClusterMock as any);
|
||||
expect(usageStats).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('with empty data', async () => {
|
||||
const mockConfig = getMockReportingConfig();
|
||||
const plugins = getPluginsMock();
|
||||
const { fetch } = getReportingUsageCollector(
|
||||
mockConfig,
|
||||
plugins.usageCollection,
|
||||
plugins.__LEGACY.plugins.xpack_main.info,
|
||||
exportTypesRegistry,
|
||||
function isReady() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
);
|
||||
const callClusterMock = jest.fn(() =>
|
||||
Promise.resolve(
|
||||
getResponseMock({
|
||||
aggregations: {
|
||||
ranges: {
|
||||
buckets: {
|
||||
all: {
|
||||
doc_count: 0,
|
||||
jobTypes: { buckets: [] },
|
||||
layoutTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
objectTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
statusByApp: { buckets: [] },
|
||||
statusTypes: { buckets: [] },
|
||||
},
|
||||
last7Days: {
|
||||
doc_count: 0,
|
||||
jobTypes: { buckets: [] },
|
||||
layoutTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
objectTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
statusByApp: { buckets: [] },
|
||||
statusTypes: { buckets: [] },
|
||||
},
|
||||
lastDay: {
|
||||
doc_count: 0,
|
||||
jobTypes: { buckets: [] },
|
||||
layoutTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
objectTypes: { doc_count: 0, pdf: { buckets: [] } },
|
||||
statusByApp: { buckets: [] },
|
||||
statusTypes: { buckets: [] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as SearchResponse)
|
||||
)
|
||||
);
|
||||
const usageStats = await fetch(callClusterMock as any);
|
||||
expect(usageStats).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -4,15 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export interface AvailableTotal {
|
||||
available: boolean;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface StatusCounts {
|
||||
[statusType: string]: number;
|
||||
}
|
||||
|
||||
export interface KeyCountBucket {
|
||||
key: string;
|
||||
doc_count: number;
|
||||
|
@ -20,22 +11,53 @@ export interface KeyCountBucket {
|
|||
|
||||
export interface AggregationBuckets {
|
||||
buckets: KeyCountBucket[];
|
||||
pdf?: {
|
||||
buckets: KeyCountBucket[];
|
||||
}
|
||||
|
||||
export interface StatusByAppBucket {
|
||||
key: string;
|
||||
doc_count: number;
|
||||
jobTypes: {
|
||||
buckets: Array<{
|
||||
doc_count: number;
|
||||
key: string;
|
||||
appNames: AggregationBuckets;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Mapped Types and Intersection Types
|
||||
*/
|
||||
|
||||
type AggregationKeys = 'jobTypes' | 'layoutTypes' | 'objectTypes' | 'statusTypes';
|
||||
export type AggregationResults = { [K in AggregationKeys]: AggregationBuckets } & {
|
||||
export interface AggregationResultBuckets {
|
||||
jobTypes: AggregationBuckets;
|
||||
layoutTypes: {
|
||||
doc_count: number;
|
||||
pdf: AggregationBuckets;
|
||||
};
|
||||
objectTypes: {
|
||||
doc_count: number;
|
||||
pdf: AggregationBuckets;
|
||||
};
|
||||
statusTypes: AggregationBuckets;
|
||||
statusByApp: {
|
||||
buckets: StatusByAppBucket[];
|
||||
};
|
||||
doc_count: number;
|
||||
}
|
||||
|
||||
type RangeAggregationKeys = 'all' | 'lastDay' | 'last7Days';
|
||||
export type RangeAggregationResults = { [K in RangeAggregationKeys]?: AggregationResults };
|
||||
export interface SearchResponse {
|
||||
aggregations: {
|
||||
ranges: {
|
||||
buckets: {
|
||||
all: AggregationResultBuckets;
|
||||
last7Days: AggregationResultBuckets;
|
||||
lastDay: AggregationResultBuckets;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface AvailableTotal {
|
||||
available: boolean;
|
||||
total: number;
|
||||
}
|
||||
|
||||
type BaseJobTypeKeys = 'csv' | 'PNG';
|
||||
export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & {
|
||||
|
@ -51,9 +73,22 @@ export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & {
|
|||
};
|
||||
};
|
||||
|
||||
interface StatusCounts {
|
||||
[statusType: string]: number;
|
||||
}
|
||||
|
||||
interface StatusByAppCounts {
|
||||
[statusType: string]: {
|
||||
[jobType: string]: {
|
||||
[appName: string]: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type RangeStats = JobTypes & {
|
||||
_all: number;
|
||||
status: StatusCounts;
|
||||
statuses: StatusByAppCounts;
|
||||
};
|
||||
|
||||
export type ExportType = 'csv' | 'printable_pdf' | 'PNG';
|
||||
|
|
Loading…
Reference in a new issue