From 2f2db4f0c5d732a66e3e7e09743bd0498eacffdf Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 16 Apr 2020 15:49:19 -0700 Subject: [PATCH 01/10] [Reporting] Additional status by app data for usage --- .../server/usage/decorate_range_stats.ts | 4 +- .../server/usage/get_reporting_usage.ts | 41 +++++++++++++----- .../plugins/reporting/server/usage/types.d.ts | 42 ++++++++++++++++--- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts index 359bcc45230c3..aad0163d9689f 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts @@ -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, @@ -47,6 +47,7 @@ export const decorateRangeStats = ( const { _all: rangeAll, status: rangeStatus, + status_by_app: 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 }, + status_by_app: rangeStatusByApp, ...rangePdf, ...rangeBasic, } as RangeStats; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index e9523d9e70202..700aa821dbea9 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -11,13 +11,13 @@ import { ReportingConfig } from '../types'; import { decorateRangeStats } from './decorate_range_stats'; import { getExportTypesHandler } from './get_export_type_handler'; import { - AggregationBuckets, - AggregationResults, FeatureAvailabilityMap, JobTypes, KeyCountBucket, - RangeAggregationResults, RangeStats, + ReportingUsage, + SearchResponse, + AggregationResultBuckets, } 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,8 +39,8 @@ 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; +function getAggStats(aggs: AggregationResultBuckets): RangeStats { + const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY]; const jobTypes: JobTypes = jobBuckets.reduce( (accum: JobTypes, { key, doc_count: count }: { key: string; doc_count: number }) => { return { @@ -74,7 +75,13 @@ function getAggStats(aggs: AggregationResults) { statusTypes = getKeyCount(statusBuckets); } - return { _all: all, status: statusTypes, ...jobTypes }; + const statusByApp = { + completed: { + canvas: 10, + }, + }; + + return { _all: all, status: statusTypes, status_by_app: statusByApp, ...jobTypes }; } type RangeStatSets = Partial< @@ -83,12 +90,13 @@ type RangeStatSets = Partial< last7Days: RangeStats; } >; -async function handleResponse(response: AggregationResults): Promise { - const buckets = get(response, 'aggregations.ranges.buckets'); + +async function handleResponse(response: SearchResponse): Promise { + const buckets = get(response, 'aggregations.ranges.buckets'); // prettier-ignore 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); @@ -106,7 +114,7 @@ export async function getReportingUsage( xpackMainInfo: XPackInfo, callCluster: ESCallCluster, exportTypesRegistry: ExportTypesRegistry -) { +): Promise { const reportingIndex = config.get('index'); const params = { @@ -126,6 +134,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: { + appName: { + terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE }, + aggs: { + jobType: { terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, + }, + }, + }, + }, [OBJECT_TYPES_KEY]: { filter: { term: { jobtype: PRINTABLE_PDF_JOBTYPE } }, aggs: { pdf: { terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } } }, @@ -141,7 +160,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! diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index 98e025ccf661e..68a73c5c28f0a 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -13,14 +13,25 @@ interface StatusCounts { [statusType: string]: number; } +interface StatusByAppCounts { + [statusType: string]: { + [appType: string]: number; + }; +} + export interface KeyCountBucket { key: string; doc_count: number; } export interface AggregationBuckets { - buckets: KeyCountBucket[]; + doc_count?: number; + doc_count_error_upper_bound?: number; + sum_other_doc_count?: number; + buckets?: any; pdf?: { + doc_count_error_upper_bound?: number; + sum_other_doc_count?: number; buckets: KeyCountBucket[]; }; } @@ -29,13 +40,22 @@ export interface AggregationBuckets { * Mapped Types and Intersection Types */ -type AggregationKeys = 'jobTypes' | 'layoutTypes' | 'objectTypes' | 'statusTypes'; -export type AggregationResults = { [K in AggregationKeys]: AggregationBuckets } & { +type AggregationKeys = 'jobTypes' | 'layoutTypes' | 'objectTypes' | 'statusTypes' | 'statusByApp'; +export type AggregationResultBuckets = { [K in AggregationKeys]: AggregationBuckets } & { 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; + }; + }; + }; +} type BaseJobTypeKeys = 'csv' | 'PNG'; export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & { @@ -54,7 +74,19 @@ export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & { export type RangeStats = JobTypes & { _all: number; status: StatusCounts; + status_by_app: StatusByAppCounts; }; export type ExportType = 'csv' | 'printable_pdf' | 'PNG'; export type FeatureAvailabilityMap = { [F in ExportType]: boolean }; + +export type ReportingUsage = { + available: boolean; + enabled: boolean; + browser_type: string; + csv: AvailableTotal; + PNG: AvailableTotal; + printable_pdf: AvailableTotal; + last7Days: RangeStats; + lastDay: RangeStats; +} & RangeStats; From 5755e8437bb3e1547e957b6014c25f90ba8315bc Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 16 Apr 2020 17:15:55 -0700 Subject: [PATCH 02/10] --wip-- [skip ci] --- .../legacy/plugins/reporting/server/usage/get_reporting_usage.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index 700aa821dbea9..c9f973dd4bac9 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -75,6 +75,7 @@ function getAggStats(aggs: AggregationResultBuckets): RangeStats { statusTypes = getKeyCount(statusBuckets); } + // FIXME const statusByApp = { completed: { canvas: 10, From 305d5df51d294de1462bd9b1121ec76d7bafeb6d Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 20 Apr 2020 12:05:10 -0700 Subject: [PATCH 03/10] clean up types --- .../reporting_usage_collector.test.ts.snap | 95 +++++++++++++ .../server/usage/get_reporting_usage.ts | 56 +++++--- .../usage/reporting_usage_collector.test.ts | 129 ++++++------------ .../plugins/reporting/server/usage/types.d.ts | 40 +++--- 4 files changed, 188 insertions(+), 132 deletions(-) create mode 100644 x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap diff --git a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap new file mode 100644 index 0000000000000..4db37bbf5b2bb --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`data modeling with normal looking usage data 1`] = ` +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, + }, + "status_by_app": Object {}, + }, + "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, + }, + "status_by_app": Object {}, + }, + "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, + }, + "status_by_app": Object {}, +} +`; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index c9f973dd4bac9..d50d9770b2359 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -11,17 +11,27 @@ import { ReportingConfig } from '../types'; import { decorateRangeStats } from './decorate_range_stats'; import { getExportTypesHandler } from './get_export_type_handler'; import { + AggregationResultBuckets, FeatureAvailabilityMap, JobTypes, KeyCountBucket, RangeStats, ReportingUsage, SearchResponse, - AggregationResultBuckets, + StatusByAppBucket, } from './types'; type XPackInfo = XPackMainPlugin['info']; +type SearchAggregation = SearchResponse['aggregations']['ranges']['buckets']; + +type RangeStatSets = Partial< + RangeStats & { + lastDay: RangeStats; + last7Days: RangeStats; + } +>; + const JOB_TYPES_KEY = 'jobTypes'; const JOB_TYPES_FIELD = 'jobtype'; const LAYOUT_TYPES_KEY = 'layoutTypes'; @@ -39,8 +49,22 @@ const PRINTABLE_PDF_JOBTYPE = 'printable_pdf'; const getKeyCount = (buckets: KeyCountBucket[]): { [key: string]: number } => buckets.reduce((accum, { key, doc_count: count }) => ({ ...accum, [key]: count }), {}); +// indexes some key/count buckets by statusType > appName: statusCount +const getAppStatuses = (buckets: StatusByAppBucket[]) => + buckets.reduce((acc, cur) => { + return { + ...acc, + [cur.key]: cur.appNames.buckets.reduce((a, c) => { + return { + ...a, + [c.key]: c.doc_count, + }; + }, {}), + }; + }, {}); + function getAggStats(aggs: AggregationResultBuckets): RangeStats { - const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY]; + 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 }) => { return { @@ -56,8 +80,8 @@ function getAggStats(aggs: AggregationResultBuckets): RangeStats { // 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(aggs[OBJECT_TYPES_KEY], '.pdf.buckets', []); + const pdfLayoutBuckets = get(aggs[LAYOUT_TYPES_KEY], '.pdf.buckets', []); pdfJobs.app = getKeyCount(pdfAppBuckets) as { visualization: number; dashboard: number; @@ -70,30 +94,22 @@ function getAggStats(aggs: AggregationResultBuckets): RangeStats { const all = aggs.doc_count as number; let statusTypes = {}; - const statusBuckets = get(aggs[STATUS_TYPES_KEY], 'buckets', []); + const statusBuckets = get(aggs[STATUS_TYPES_KEY], 'buckets', []); if (statusBuckets) { statusTypes = getKeyCount(statusBuckets); } - // FIXME - const statusByApp = { - completed: { - canvas: 10, - }, - }; + let statusByApp = {}; + const statusAppBuckets = get(aggs[STATUS_BY_APP_KEY], 'buckets', []); + if (statusAppBuckets) { + statusByApp = getAppStatuses(statusAppBuckets); + } return { _all: all, status: statusTypes, status_by_app: statusByApp, ...jobTypes }; } -type RangeStatSets = Partial< - RangeStats & { - lastDay: RangeStats; - last7Days: RangeStats; - } ->; - async function handleResponse(response: SearchResponse): Promise { - const buckets = get(response, 'aggregations.ranges.buckets'); // prettier-ignore + const buckets = get(response, 'aggregations.ranges.buckets'); if (!buckets) { return {}; } @@ -138,7 +154,7 @@ export async function getReportingUsage( [STATUS_BY_APP_KEY]: { terms: { field: 'status', size: DEFAULT_TERMS_SIZE }, aggs: { - appName: { + appNames: { terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE }, aggs: { jobType: { terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts index dbc674ce36ec8..3c0eb2a272571 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -61,7 +61,7 @@ const getMockReportingConfig = () => ({ get: () => {}, kbnConfig: { get: () => '' }, }); -const getResponseMock = (customization = {}) => customization; +const getResponseMock = (base = {}) => base; describe('license checks', () => { let mockConfig: ReportingConfig; @@ -322,96 +322,47 @@ describe('data modeling', () => { ); 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, + expect(usageStats).toMatchSnapshot(); + }); + + test('status by app', 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: { + smashTypes: { + 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 }, + ], + }, + }, + }, }, - "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, - }, - } - `); + }) + ) + ); + + const usageStats = await fetch(callClusterMock as any); + expect(usageStats).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index 68a73c5c28f0a..465d211ddd41a 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -9,38 +9,22 @@ export interface AvailableTotal { total: number; } -interface StatusCounts { - [statusType: string]: number; -} - -interface StatusByAppCounts { - [statusType: string]: { - [appType: string]: number; - }; -} - export interface KeyCountBucket { key: string; doc_count: number; } -export interface AggregationBuckets { - doc_count?: number; - doc_count_error_upper_bound?: number; - sum_other_doc_count?: number; - buckets?: any; - pdf?: { - doc_count_error_upper_bound?: number; - sum_other_doc_count?: number; - buckets: KeyCountBucket[]; - }; +export interface StatusByAppBucket { + key: string; + appNames: { key: string; buckets: KeyCountBucket[] }; } -/* - * Mapped Types and Intersection Types - */ +export interface AggregationBuckets { + buckets?: Array<{ key: string; doc_count: number }>; +} type AggregationKeys = 'jobTypes' | 'layoutTypes' | 'objectTypes' | 'statusTypes' | 'statusByApp'; + export type AggregationResultBuckets = { [K in AggregationKeys]: AggregationBuckets } & { doc_count: number; }; @@ -71,6 +55,16 @@ export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & { }; }; +interface StatusCounts { + [statusType: string]: number; +} + +interface StatusByAppCounts { + [statusType: string]: { + [appType: string]: number; + }; +} + export type RangeStats = JobTypes & { _all: number; status: StatusCounts; From 949c0b5b5c1307addd93b286e347480ead62810c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 20 Apr 2020 13:19:52 -0700 Subject: [PATCH 04/10] add a prettier-ignore --- .../usage/reporting_usage_collector.test.ts | 117 +++--------------- 1 file changed, 16 insertions(+), 101 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 3c0eb2a272571..fd387530465b1 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -207,117 +207,32 @@ describe('data modeling', () => { 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 }, - ], - }, + 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 }, ], }, + statusByApp: {}, }, 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 }, - ], - }, + 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 }, ], }, + statusByApp: {}, }, 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 }, - ], - }, - }, - 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 }, - ], - }, + 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 }, ], }, }, + 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 }, ], }, + statusByApp: {}, }, }, }, }, - }) + } as SearchResponse) // prettier-ignore ) ); From 468bde552f5d5bb4af6d7807f9107695d61eff12 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 20 Apr 2020 13:20:20 -0700 Subject: [PATCH 05/10] fix types --- .../reporting_usage_collector.test.ts.snap | 102 ++++++++++++------ .../server/usage/get_reporting_usage.ts | 35 +++--- .../usage/reporting_usage_collector.test.ts | 68 +----------- .../plugins/reporting/server/usage/types.d.ts | 60 ++++++----- 4 files changed, 119 insertions(+), 146 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index 4db37bbf5b2bb..c64bb567335b8 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -4,92 +4,124 @@ exports[`data modeling with normal looking usage data 1`] = ` Object { "PNG": Object { "available": true, - "total": 4, + "total": 25, }, - "_all": 54, + "_all": 40, "available": true, "browser_type": undefined, "csv": Object { "available": true, - "total": 27, + "total": 0, }, "enabled": true, "last7Days": Object { "PNG": Object { "available": true, - "total": 4, + "total": 6, }, - "_all": 27, + "_all": 15, "csv": Object { "available": true, - "total": 10, + "total": 0, }, "printable_pdf": Object { "app": Object { - "dashboard": 13, - "visualization": 0, + "canvas workpad": 8, + "dashboard": 0, + "visualization": 1, }, "available": true, "layout": Object { - "preserve_layout": 3, - "print": 10, + "preserve_layout": 9, + "print": 0, }, - "total": 13, + "total": 9, }, "status": Object { - "completed": 0, + "completed": 12, + "completed_with_warnings": 3, "failed": 0, - "pending": 27, }, - "status_by_app": Object {}, + "status_by_app": Object { + "completed": Object { + "canvas workpad": 7, + "dashboard": 4, + "visualization": 1, + }, + "completed_with_warnings": Object { + "canvas workpad": 1, + "dashboard": 2, + }, + }, }, "lastDay": Object { "PNG": Object { "available": true, - "total": 4, + "total": 6, }, - "_all": 11, + "_all": 7, "csv": Object { "available": true, - "total": 5, + "total": 0, }, "printable_pdf": Object { "app": Object { - "dashboard": 2, - "visualization": 0, + "dashboard": 0, + "visualization": 1, }, "available": true, "layout": Object { - "preserve_layout": 0, - "print": 2, + "preserve_layout": 1, + "print": 0, }, - "total": 2, + "total": 1, }, "status": Object { - "completed": 0, + "completed": 5, + "completed_with_warnings": 2, "failed": 0, - "pending": 11, }, - "status_by_app": Object {}, + "status_by_app": Object { + "completed": Object { + "dashboard": 4, + "visualization": 1, + }, + "completed_with_warnings": Object { + "dashboard": 2, + }, + }, }, "printable_pdf": Object { "app": Object { - "dashboard": 23, - "visualization": 0, + "canvas workpad": 10, + "dashboard": 0, + "visualization": 5, }, "available": true, "layout": Object { - "preserve_layout": 13, - "print": 10, + "preserve_layout": 15, + "print": 0, }, - "total": 23, + "total": 15, }, "status": Object { - "completed": 20, - "failed": 0, - "pending": 33, - "processing": 1, + "completed": 34, + "completed_with_warnings": 3, + "failed": 3, + }, + "status_by_app": Object { + "completed": Object { + "canvas workpad": 9, + "dashboard": 4, + "visualization": 21, + }, + "completed_with_warnings": Object { + "canvas workpad": 1, + "dashboard": 2, + }, + "failed": Object { + "visualization": 3, + }, }, - "status_by_app": Object {}, } `; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index d50d9770b2359..5c849705bc233 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -16,22 +16,12 @@ import { JobTypes, KeyCountBucket, RangeStats, - ReportingUsage, SearchResponse, StatusByAppBucket, } from './types'; type XPackInfo = XPackMainPlugin['info']; -type SearchAggregation = SearchResponse['aggregations']['ranges']['buckets']; - -type RangeStatSets = Partial< - RangeStats & { - lastDay: RangeStats; - last7Days: RangeStats; - } ->; - const JOB_TYPES_KEY = 'jobTypes'; const JOB_TYPES_FIELD = 'jobtype'; const LAYOUT_TYPES_KEY = 'layoutTypes'; @@ -64,15 +54,10 @@ const getAppStatuses = (buckets: StatusByAppBucket[]) => }, {}); function getAggStats(aggs: AggregationResultBuckets): RangeStats { - const { buckets: jobBuckets } = aggs[JOB_TYPES_KEY] as AggregationBuckets; - const jobTypes: JobTypes = jobBuckets.reduce( + 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, - }, - }; + return { ...accum, [key]: { total: count } }; }, {} as JobTypes ); @@ -108,11 +93,17 @@ function getAggStats(aggs: AggregationResultBuckets): RangeStats { return { _all: all, status: statusTypes, status_by_app: statusByApp, ...jobTypes }; } +type SearchAggregation = SearchResponse['aggregations']['ranges']['buckets']; + +type RangeStatSets = Partial< + RangeStats & { + lastDay: RangeStats; + last7Days: RangeStats; + } +>; + async function handleResponse(response: SearchResponse): Promise { const buckets = get(response, 'aggregations.ranges.buckets'); - if (!buckets) { - return {}; - } const { lastDay, last7Days, all } = buckets; const lastDayUsage = lastDay ? getAggStats(lastDay) : ({} as RangeStats); @@ -131,7 +122,7 @@ export async function getReportingUsage( xpackMainInfo: XPackInfo, callCluster: ESCallCluster, exportTypesRegistry: ExportTypesRegistry -): Promise { +) { const reportingIndex = config.get('index'); const params = { diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts index fd387530465b1..60084bd0bb969 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -12,6 +12,7 @@ import { getReportingUsageCollector, } from './reporting_usage_collector'; import { ReportingConfig } from '../types'; +import { SearchResponse } from './types'; const exportTypesRegistry = getExportTypesRegistry(); @@ -205,30 +206,9 @@ describe('data modeling', () => { aggregations: { 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 }, ], }, - statusByApp: {}, - }, - 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 }, ], }, - statusByApp: {}, - }, - 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 }, ], }, }, - 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 }, ], }, - statusByApp: {}, - }, + all: { doc_count: 40, layoutTypes: { doc_count: 15, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 15 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 34, appNames: { buckets: [ { key: 'visualization', doc_count: 21, jobType: { buckets: [ { key: 'PNG', doc_count: 16 }, { key: 'printable_pdf', doc_count: 5 }, ], }, }, { key: 'canvas workpad', doc_count: 9, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 9 }] }, }, { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 3, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, { key: 'canvas workpad', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'failed', doc_count: 3, appNames: { buckets: [ { key: 'visualization', doc_count: 3, jobType: { buckets: [{ key: 'PNG', doc_count: 3 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 15, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 10 }, { key: 'visualization', doc_count: 5 }, ], }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 34 }, { key: 'completed_with_warnings', doc_count: 3 }, { key: 'failed', doc_count: 3 }, ], }, jobTypes: { buckets: [ { key: 'PNG', doc_count: 25 }, { key: 'printable_pdf', doc_count: 15 }, ], }, }, + last7Days: { doc_count: 15, layoutTypes: { doc_count: 9, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 9 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 12, appNames: { buckets: [ { key: 'canvas workpad', doc_count: 7, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 7 }] }, }, { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, { key: 'visualization', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 3, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, { key: 'canvas workpad', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 9, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 8 }, { key: 'visualization', doc_count: 1 }, ], }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 12 }, { key: 'completed_with_warnings', doc_count: 3 }, ], }, jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 9 }, { key: 'PNG', doc_count: 6 }, ], }, }, + lastDay: { doc_count: 7, layoutTypes: { doc_count: 1, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 1 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 5, appNames: { buckets: [ { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, { key: 'visualization', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 2, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 1, pdf: { buckets: [{ key: 'visualization', doc_count: 1 }] }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 5 }, { key: 'completed_with_warnings', doc_count: 2 }, ], }, jobTypes: { buckets: [ { key: 'PNG', doc_count: 6 }, { key: 'printable_pdf', doc_count: 1 }, ], }, }, }, }, }, @@ -239,46 +219,6 @@ describe('data modeling', () => { const usageStats = await fetch(callClusterMock as any); expect(usageStats).toMatchSnapshot(); }); - - test('status by app', 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: { - smashTypes: { - 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 }, - ], - }, - }, - }, - }, - }, - }) - ) - ); - - const usageStats = await fetch(callClusterMock as any); - expect(usageStats).toMatchSnapshot(); - }); }); describe('Ready for collection observable', () => { diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index 465d211ddd41a..7ea999971542c 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -4,30 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface AvailableTotal { - available: boolean; - total: number; -} - export interface KeyCountBucket { key: string; doc_count: number; } -export interface StatusByAppBucket { - key: string; - appNames: { key: string; buckets: KeyCountBucket[] }; -} - export interface AggregationBuckets { - buckets?: Array<{ key: string; doc_count: number }>; + buckets: KeyCountBucket[]; } -type AggregationKeys = 'jobTypes' | 'layoutTypes' | 'objectTypes' | 'statusTypes' | 'statusByApp'; - -export type AggregationResultBuckets = { [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: Array<{ + key: string; + doc_count: number; + appNames: { + buckets: Array<{ + doc_count: number; + key: string; + jobType: AggregationBuckets; + }>; + }; + }>; + }; doc_count: number; -}; +} export interface SearchResponse { aggregations: { @@ -41,6 +52,11 @@ export interface SearchResponse { }; } +export interface AvailableTotal { + available: boolean; + total: number; +} + type BaseJobTypeKeys = 'csv' | 'PNG'; export type JobTypes = { [K in BaseJobTypeKeys]: AvailableTotal } & { printable_pdf: AvailableTotal & { @@ -74,13 +90,7 @@ export type RangeStats = JobTypes & { export type ExportType = 'csv' | 'printable_pdf' | 'PNG'; export type FeatureAvailabilityMap = { [F in ExportType]: boolean }; -export type ReportingUsage = { - available: boolean; - enabled: boolean; - browser_type: string; - csv: AvailableTotal; - PNG: AvailableTotal; - printable_pdf: AvailableTotal; - last7Days: RangeStats; - lastDay: RangeStats; -} & RangeStats; +export interface StatusByAppBucket { + key: string; + appNames: { key: string; buckets: KeyCountBucket[] }; +} From 5a9af9af2c8df00c362db131e0c09f005d513e4c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 21 Apr 2020 10:51:11 -0700 Subject: [PATCH 06/10] --wip-- [skip ci] --- .../plugins/reporting/server/usage/types.d.ts | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index 7ea999971542c..fa2891a523d9e 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -13,6 +13,18 @@ export interface AggregationBuckets { buckets: KeyCountBucket[]; } +export interface StatusByAppBucket { + key: string; + doc_count: number; + appNames: { + buckets: Array<{ + doc_count: number; + key: string; + jobType: AggregationBuckets; + }>; + }; +} + export interface AggregationResultBuckets { jobTypes: AggregationBuckets; layoutTypes: { @@ -25,17 +37,7 @@ export interface AggregationResultBuckets { }; statusTypes: AggregationBuckets; statusByApp: { - buckets: Array<{ - key: string; - doc_count: number; - appNames: { - buckets: Array<{ - doc_count: number; - key: string; - jobType: AggregationBuckets; - }>; - }; - }>; + buckets: StatusByAppBucket[]; }; doc_count: number; } @@ -77,7 +79,9 @@ interface StatusCounts { interface StatusByAppCounts { [statusType: string]: { - [appType: string]: number; + [appType: string]: { + [jobType: string]: number; + }; }; } @@ -89,8 +93,3 @@ export type RangeStats = JobTypes & { export type ExportType = 'csv' | 'printable_pdf' | 'PNG'; export type FeatureAvailabilityMap = { [F in ExportType]: boolean }; - -export interface StatusByAppBucket { - key: string; - appNames: { key: string; buckets: KeyCountBucket[] }; -} From 89350d3564f35725a44a3853a0ff7df8e783c2ed Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 21 Apr 2020 11:07:52 -0700 Subject: [PATCH 07/10] fix typo --- .../plugins/reporting/server/usage/get_reporting_usage.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index 5c849705bc233..a7ceb3290f206 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -104,6 +104,9 @@ type RangeStatSets = Partial< async function handleResponse(response: SearchResponse): Promise { const buckets = get(response, 'aggregations.ranges.buckets'); + if (!buckets) { + return {}; + } const { lastDay, last7Days, all } = buckets; const lastDayUsage = lastDay ? getAggStats(lastDay) : ({} as RangeStats); From b564dc9a74617a9e4d3659a4bdcb065e5e8aae72 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 21 Apr 2020 11:29:08 -0700 Subject: [PATCH 08/10] more tests --- .../reporting_usage_collector.test.ts.snap | 186 ++++++++++++++++++ .../usage/reporting_usage_collector.test.ts | 64 ++++++ 2 files changed, 250 insertions(+) diff --git a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index c64bb567335b8..1aeb730f857cf 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -1,5 +1,95 @@ // 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, + }, + "status_by_app": 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, + }, + "status_by_app": 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, + }, + "status_by_app": Object {}, +} +`; + exports[`data modeling with normal looking usage data 1`] = ` Object { "PNG": Object { @@ -125,3 +215,99 @@ Object { }, } `; + +exports[`data modeling with sparse data 1`] = ` +Object { + "PNG": Object { + "available": true, + "total": 0, + }, + "_all": 1, + "available": true, + "browser_type": undefined, + "csv": Object { + "available": true, + "total": 1, + }, + "enabled": true, + "last7Days": Object { + "PNG": Object { + "available": true, + "total": 0, + }, + "_all": 1, + "csv": Object { + "available": true, + "total": 1, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 0, + "print": 0, + }, + "total": 0, + }, + "status": Object { + "completed": 1, + "failed": 0, + }, + "status_by_app": Object { + "completed": Object {}, + }, + }, + "lastDay": Object { + "PNG": Object { + "available": true, + "total": 0, + }, + "_all": 1, + "csv": Object { + "available": true, + "total": 1, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 0, + "print": 0, + }, + "total": 0, + }, + "status": Object { + "completed": 1, + "failed": 0, + }, + "status_by_app": Object { + "completed": Object {}, + }, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 0, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 0, + "print": 0, + }, + "total": 0, + }, + "status": Object { + "completed": 1, + "failed": 0, + }, + "status_by_app": Object { + "completed": Object {}, + }, +} +`; diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 60084bd0bb969..a91827ed82f9a 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -219,6 +219,70 @@ describe('data modeling', () => { const usageStats = await fetch(callClusterMock as any); 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: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, + last7Days: { doc_count: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, + lastDay: { doc_count: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, + }, + }, + }, + } 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) // prettier-ignore + ) + ); + + const usageStats = await fetch(callClusterMock as any); + expect(usageStats).toMatchSnapshot(); + }); }); describe('Ready for collection observable', () => { From 4f83a0e42d6bf068a97c44c5c5d06d9946666ce2 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 21 Apr 2020 12:10:46 -0700 Subject: [PATCH 09/10] Tweak the data model --- .../reporting_usage_collector.test.ts.snap | 174 ++++++++++-------- .../server/usage/decorate_range_stats.ts | 4 +- .../server/usage/get_reporting_usage.ts | 25 ++- .../usage/reporting_usage_collector.test.ts | 84 +++++++-- .../plugins/reporting/server/usage/types.d.ts | 6 +- 5 files changed, 195 insertions(+), 98 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index 1aeb730f857cf..aa22c3f66df18 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/legacy/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -40,7 +40,7 @@ Object { "completed": 0, "failed": 0, }, - "status_by_app": Object {}, + "statuses": Object {}, }, "lastDay": Object { "PNG": Object { @@ -68,7 +68,7 @@ Object { "completed": 0, "failed": 0, }, - "status_by_app": Object {}, + "statuses": Object {}, }, "printable_pdf": Object { "app": Object { @@ -86,7 +86,7 @@ Object { "completed": 0, "failed": 0, }, - "status_by_app": Object {}, + "statuses": Object {}, } `; @@ -94,9 +94,9 @@ exports[`data modeling with normal looking usage data 1`] = ` Object { "PNG": Object { "available": true, - "total": 25, + "total": 3, }, - "_all": 40, + "_all": 12, "available": true, "browser_type": undefined, "csv": Object { @@ -107,49 +107,44 @@ Object { "last7Days": Object { "PNG": Object { "available": true, - "total": 6, + "total": 1, }, - "_all": 15, + "_all": 1, "csv": Object { "available": true, "total": 0, }, "printable_pdf": Object { "app": Object { - "canvas workpad": 8, "dashboard": 0, - "visualization": 1, + "visualization": 0, }, "available": true, "layout": Object { - "preserve_layout": 9, + "preserve_layout": 0, "print": 0, }, - "total": 9, + "total": 0, }, "status": Object { - "completed": 12, - "completed_with_warnings": 3, + "completed": 0, + "completed_with_warnings": 1, "failed": 0, }, - "status_by_app": Object { - "completed": Object { - "canvas workpad": 7, - "dashboard": 4, - "visualization": 1, - }, + "statuses": Object { "completed_with_warnings": Object { - "canvas workpad": 1, - "dashboard": 2, + "PNG": Object { + "dashboard": 1, + }, }, }, }, "lastDay": Object { "PNG": Object { "available": true, - "total": 6, + "total": 1, }, - "_all": 7, + "_all": 1, "csv": Object { "available": true, "total": 0, @@ -157,60 +152,65 @@ Object { "printable_pdf": Object { "app": Object { "dashboard": 0, - "visualization": 1, + "visualization": 0, }, "available": true, "layout": Object { - "preserve_layout": 1, + "preserve_layout": 0, "print": 0, }, - "total": 1, + "total": 0, }, "status": Object { - "completed": 5, - "completed_with_warnings": 2, + "completed": 0, + "completed_with_warnings": 1, "failed": 0, }, - "status_by_app": Object { - "completed": Object { - "dashboard": 4, - "visualization": 1, - }, + "statuses": Object { "completed_with_warnings": Object { - "dashboard": 2, + "PNG": Object { + "dashboard": 1, + }, }, }, }, "printable_pdf": Object { "app": Object { - "canvas workpad": 10, + "canvas workpad": 6, "dashboard": 0, - "visualization": 5, + "visualization": 3, }, "available": true, "layout": Object { - "preserve_layout": 15, + "preserve_layout": 9, "print": 0, }, - "total": 15, + "total": 9, }, "status": Object { - "completed": 34, - "completed_with_warnings": 3, - "failed": 3, + "completed": 10, + "completed_with_warnings": 1, + "failed": 1, }, - "status_by_app": Object { + "statuses": Object { "completed": Object { - "canvas workpad": 9, - "dashboard": 4, - "visualization": 21, + "PNG": Object { + "visualization": 1, + }, + "printable_pdf": Object { + "canvas workpad": 6, + "visualization": 3, + }, }, "completed_with_warnings": Object { - "canvas workpad": 1, - "dashboard": 2, + "PNG": Object { + "dashboard": 1, + }, }, "failed": Object { - "visualization": 3, + "PNG": Object { + "dashboard": 1, + }, }, }, } @@ -220,9 +220,9 @@ exports[`data modeling with sparse data 1`] = ` Object { "PNG": Object { "available": true, - "total": 0, + "total": 1, }, - "_all": 1, + "_all": 4, "available": true, "browser_type": undefined, "csv": Object { @@ -233,81 +233,111 @@ Object { "last7Days": Object { "PNG": Object { "available": true, - "total": 0, + "total": 1, }, - "_all": 1, + "_all": 4, "csv": Object { "available": true, "total": 1, }, "printable_pdf": Object { "app": Object { - "dashboard": 0, + "canvas workpad": 1, + "dashboard": 1, "visualization": 0, }, "available": true, "layout": Object { - "preserve_layout": 0, + "preserve_layout": 2, "print": 0, }, - "total": 0, + "total": 2, }, "status": Object { - "completed": 1, + "completed": 4, "failed": 0, }, - "status_by_app": Object { - "completed": Object {}, + "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": 0, + "total": 1, }, - "_all": 1, + "_all": 4, "csv": Object { "available": true, "total": 1, }, "printable_pdf": Object { "app": Object { - "dashboard": 0, + "canvas workpad": 1, + "dashboard": 1, "visualization": 0, }, "available": true, "layout": Object { - "preserve_layout": 0, + "preserve_layout": 2, "print": 0, }, - "total": 0, + "total": 2, }, "status": Object { - "completed": 1, + "completed": 4, "failed": 0, }, - "status_by_app": Object { - "completed": Object {}, + "statuses": Object { + "completed": Object { + "PNG": Object { + "dashboard": 1, + }, + "csv": Object {}, + "printable_pdf": Object { + "canvas workpad": 1, + "dashboard": 1, + }, + }, }, }, "printable_pdf": Object { "app": Object { - "dashboard": 0, + "canvas workpad": 1, + "dashboard": 1, "visualization": 0, }, "available": true, "layout": Object { - "preserve_layout": 0, + "preserve_layout": 2, "print": 0, }, - "total": 0, + "total": 2, }, "status": Object { - "completed": 1, + "completed": 4, "failed": 0, }, - "status_by_app": Object { - "completed": Object {}, + "statuses": Object { + "completed": Object { + "PNG": Object { + "dashboard": 1, + }, + "csv": Object {}, + "printable_pdf": Object { + "canvas workpad": 1, + "dashboard": 1, + }, + }, }, } `; diff --git a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts index aad0163d9689f..ef985d2dd1cf3 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts @@ -47,7 +47,7 @@ export const decorateRangeStats = ( const { _all: rangeAll, status: rangeStatus, - status_by_app: rangeStatusByApp, + statuses: rangeStatusByApp, [PDF_JOB_TYPE]: rangeStatsPdf, ...rangeStatsBasic } = rangeStats; @@ -74,7 +74,7 @@ export const decorateRangeStats = ( const resultStats = { _all: rangeAll || 0, status: { completed: 0, failed: 0, ...rangeStatus }, - status_by_app: rangeStatusByApp, + statuses: rangeStatusByApp, ...rangePdf, ...rangeBasic, } as RangeStats; diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index a7ceb3290f206..1ee2e2e2d6844 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -39,15 +39,20 @@ const PRINTABLE_PDF_JOBTYPE = 'printable_pdf'; const getKeyCount = (buckets: KeyCountBucket[]): { [key: string]: number } => buckets.reduce((accum, { key, doc_count: count }) => ({ ...accum, [key]: count }), {}); -// indexes some key/count buckets by statusType > appName: statusCount +// indexes some key/count buckets by statusType > appName > jobType: statusCount const getAppStatuses = (buckets: StatusByAppBucket[]) => - buckets.reduce((acc, cur) => { + buckets.reduce((statuses, statusBucket) => { return { - ...acc, - [cur.key]: cur.appNames.buckets.reduce((a, c) => { + ...statuses, + [statusBucket.key]: statusBucket.jobTypes.buckets.reduce((jobTypes, job) => { return { - ...a, - [c.key]: c.doc_count, + ...jobTypes, + [job.key]: job.appNames.buckets.reduce((apps, app) => { + return { + ...apps, + [app.key]: app.doc_count, + }; + }, {}), }; }, {}), }; @@ -90,7 +95,7 @@ function getAggStats(aggs: AggregationResultBuckets): RangeStats { statusByApp = getAppStatuses(statusAppBuckets); } - return { _all: all, status: statusTypes, status_by_app: statusByApp, ...jobTypes }; + return { _all: all, status: statusTypes, statuses: statusByApp, ...jobTypes }; } type SearchAggregation = SearchResponse['aggregations']['ranges']['buckets']; @@ -148,10 +153,10 @@ export async function getReportingUsage( [STATUS_BY_APP_KEY]: { terms: { field: 'status', size: DEFAULT_TERMS_SIZE }, aggs: { - appNames: { - terms: { field: OBJECT_TYPES_FIELD, size: DEFAULT_TERMS_SIZE }, + jobTypes: { + terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE }, aggs: { - jobType: { terms: { field: JOB_TYPES_FIELD, size: DEFAULT_TERMS_SIZE } }, + 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 }, }, }, diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts index a91827ed82f9a..61b736a3e4d8c 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -206,9 +206,30 @@ describe('data modeling', () => { aggregations: { ranges: { buckets: { - all: { doc_count: 40, layoutTypes: { doc_count: 15, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 15 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 34, appNames: { buckets: [ { key: 'visualization', doc_count: 21, jobType: { buckets: [ { key: 'PNG', doc_count: 16 }, { key: 'printable_pdf', doc_count: 5 }, ], }, }, { key: 'canvas workpad', doc_count: 9, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 9 }] }, }, { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 3, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, { key: 'canvas workpad', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'failed', doc_count: 3, appNames: { buckets: [ { key: 'visualization', doc_count: 3, jobType: { buckets: [{ key: 'PNG', doc_count: 3 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 15, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 10 }, { key: 'visualization', doc_count: 5 }, ], }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 34 }, { key: 'completed_with_warnings', doc_count: 3 }, { key: 'failed', doc_count: 3 }, ], }, jobTypes: { buckets: [ { key: 'PNG', doc_count: 25 }, { key: 'printable_pdf', doc_count: 15 }, ], }, }, - last7Days: { doc_count: 15, layoutTypes: { doc_count: 9, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 9 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 12, appNames: { buckets: [ { key: 'canvas workpad', doc_count: 7, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 7 }] }, }, { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, { key: 'visualization', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 3, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, { key: 'canvas workpad', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 9, pdf: { buckets: [ { key: 'canvas workpad', doc_count: 8 }, { key: 'visualization', doc_count: 1 }, ], }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 12 }, { key: 'completed_with_warnings', doc_count: 3 }, ], }, jobTypes: { buckets: [ { key: 'printable_pdf', doc_count: 9 }, { key: 'PNG', doc_count: 6 }, ], }, }, - lastDay: { doc_count: 7, layoutTypes: { doc_count: 1, pdf: { buckets: [{ key: 'preserve_layout', doc_count: 1 }] }, }, statusByApp: { buckets: [ { key: 'completed', doc_count: 5, appNames: { buckets: [ { key: 'dashboard', doc_count: 4, jobType: { buckets: [{ key: 'PNG', doc_count: 4 }] }, }, { key: 'visualization', doc_count: 1, jobType: { buckets: [{ key: 'printable_pdf', doc_count: 1 }] }, }, ], }, }, { key: 'completed_with_warnings', doc_count: 2, appNames: { buckets: [ { key: 'dashboard', doc_count: 2, jobType: { buckets: [{ key: 'PNG', doc_count: 2 }] }, }, ], }, }, ], }, objectTypes: { doc_count: 1, pdf: { buckets: [{ key: 'visualization', doc_count: 1 }] }, }, statusTypes: { buckets: [ { key: 'completed', doc_count: 5 }, { key: 'completed_with_warnings', doc_count: 2 }, ], }, jobTypes: { buckets: [ { key: 'PNG', doc_count: 6 }, { key: 'printable_pdf', doc_count: 1 }, ], }, }, + all: { + 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: 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' }] }, + }, + 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' }] }, + }, }, }, }, @@ -238,9 +259,30 @@ describe('data modeling', () => { aggregations: { ranges: { buckets: { - all: { doc_count: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, - last7Days: { doc_count: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, - lastDay: { doc_count: 1, jobTypes: { buckets: [ { doc_count: 1, key: 'csv', }, ], }, layoutTypes: { doc_count: 0, pdf: { buckets: [], }, }, objectTypes: { doc_count: 0, pdf: { buckets: [], }, }, statusByApp: { buckets: [ { appNames: { buckets: [], }, doc_count: 1, key: 'completed', }, ], }, statusTypes: { buckets: [ { doc_count: 1, key: 'completed', }, ], }, }, + 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 }, ], }, + }, }, }, }, @@ -270,16 +312,36 @@ describe('data modeling', () => { 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: [], }, }, + 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) // prettier-ignore + } as SearchResponse) ) ); - const usageStats = await fetch(callClusterMock as any); expect(usageStats).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index fa2891a523d9e..91a12bfb2799d 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -16,11 +16,11 @@ export interface AggregationBuckets { export interface StatusByAppBucket { key: string; doc_count: number; - appNames: { + jobTypes: { buckets: Array<{ doc_count: number; key: string; - jobType: AggregationBuckets; + appNames: AggregationBuckets; }>; }; } @@ -88,7 +88,7 @@ interface StatusByAppCounts { export type RangeStats = JobTypes & { _all: number; status: StatusCounts; - status_by_app: StatusByAppCounts; + statuses: StatusByAppCounts; }; export type ExportType = 'csv' | 'printable_pdf' | 'PNG'; From 240b13fac0a92027629172dcc6958d186193c174 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 23 Apr 2020 12:23:34 -0700 Subject: [PATCH 10/10] fix the comments and type keys to reflect the data model --- .../plugins/reporting/server/usage/get_reporting_usage.ts | 2 +- x-pack/legacy/plugins/reporting/server/usage/types.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts index 1ee2e2e2d6844..2c3bb8f4bf71c 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/get_reporting_usage.ts @@ -39,7 +39,7 @@ const PRINTABLE_PDF_JOBTYPE = 'printable_pdf'; const getKeyCount = (buckets: KeyCountBucket[]): { [key: string]: number } => buckets.reduce((accum, { key, doc_count: count }) => ({ ...accum, [key]: count }), {}); -// indexes some key/count buckets by statusType > appName > jobType: statusCount +// indexes some key/count buckets by statusType > jobType > appName: statusCount const getAppStatuses = (buckets: StatusByAppBucket[]) => buckets.reduce((statuses, statusBucket) => { return { diff --git a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts index 91a12bfb2799d..83f1701863355 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/types.d.ts @@ -79,8 +79,8 @@ interface StatusCounts { interface StatusByAppCounts { [statusType: string]: { - [appType: string]: { - [jobType: string]: number; + [jobType: string]: { + [appName: string]: number; }; }; }