Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/plugins/telemetry/schema/oss_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4084,7 +4084,7 @@
}
}
}
},
},
"security_account": {
"properties": {
"appId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,41 @@ export interface CustomElementTelemetry {

export const customElementSchema: MakeSchemaFrom<CustomElementTelemetry> = {
custom_elements: {
count: { type: 'long' },
count: {
type: 'long',
_meta: {
description: 'The total number of custom Canvas elements',
},
},
elements: {
min: { type: 'long' },
max: { type: 'long' },
avg: { type: 'float' },
min: {
type: 'long',
_meta: {
description: 'The minimum number of elements used across all Canvas Custom Elements',
},
},
max: {
type: 'long',
_meta: {
description: 'The maximum number of elements used across all Canvas Custom Elements',
},
},
avg: {
type: 'float',
_meta: {
description: 'The average number of elements used in Canvas Custom Element',
},
},
},
functions_in_use: {
type: 'array',
items: {
type: 'keyword',
_meta: {
description: 'The functions in use by Canvas Custom Elements',
},
},
},
functions_in_use: { type: 'array', items: { type: 'keyword' } },
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { cloneDeep } from 'lodash';
import { summarizeWorkpads } from './workpad_collector';
import { workpads } from '../../__fixtures__/workpads';
import moment from 'moment';

describe('usage collector handle es response data', () => {
it('should summarize workpads, pages, and elements', () => {
Expand Down Expand Up @@ -49,6 +50,8 @@ describe('usage collector handle es response data', () => {
'image',
'shape',
],
in_use_30d: [],
in_use_90d: [],
},
variables: {
total: 7,
Expand All @@ -71,7 +74,13 @@ describe('usage collector handle es response data', () => {
workpads: { total: 1 },
pages: { total: 1, per_workpad: { avg: 1, min: 1, max: 1 } },
elements: { total: 1, per_page: { avg: 1, min: 1, max: 1 } },
functions: { total: 1, in_use: ['toast'], per_element: { avg: 1, min: 1, max: 1 } },
functions: {
total: 1,
in_use: ['toast'],
in_use_30d: [],
in_use_90d: [],
per_element: { avg: 1, min: 1, max: 1 },
},
variables: { total: 1, per_workpad: { avg: 1, min: 1, max: 1 } },
});
});
Expand Down Expand Up @@ -116,6 +125,8 @@ describe('usage collector handle es response data', () => {
'plot',
'seriesStyle',
],
in_use_30d: [],
in_use_90d: [],
per_element: { avg: 7, min: 7, max: 7 },
},
variables: { total: 0, per_workpad: { avg: 0, min: 0, max: 0 } }, // Variables still possible even with no pages
Expand All @@ -126,4 +137,42 @@ describe('usage collector handle es response data', () => {
const usage = summarizeWorkpads([]);
expect(usage).toEqual({});
});

describe('functions', () => {
it('collects funtions used in the most recent 30d and 90d', () => {
const thirtyDayFunction = '30d';
const ninetyDayFunction = '90d';
const otherFunction = '180d';

const workpad30d = cloneDeep(workpads[0]);
const workpad90d = cloneDeep(workpads[0]);
const workpad180d = cloneDeep(workpads[0]);

const now = moment();

workpad30d['@timestamp'] = now.subtract(1, 'day').toDate().toISOString();
workpad90d['@timestamp'] = now.subtract(80, 'day').toDate().toISOString();
workpad180d['@timestamp'] = now.subtract(180, 'day').toDate().toISOString();

workpad30d.pages[0].elements[0].expression = `${thirtyDayFunction}`;
workpad90d.pages[0].elements[0].expression = `${ninetyDayFunction}`;
workpad180d.pages[0].elements[0].expression = `${otherFunction}`;

const mockWorkpads = [workpad30d, workpad90d, workpad180d];
const usage = summarizeWorkpads(mockWorkpads);

expect(usage.functions?.in_use_30d).toHaveLength(1);
expect(usage.functions?.in_use_30d).toEqual(expect.arrayContaining([thirtyDayFunction]));

expect(usage.functions?.in_use_90d).toHaveLength(2);
expect(usage.functions?.in_use_90d).toEqual(
expect.arrayContaining([thirtyDayFunction, ninetyDayFunction])
);

expect(usage.functions?.in_use).toHaveLength(3);
expect(usage.functions?.in_use).toEqual(
expect.arrayContaining([thirtyDayFunction, ninetyDayFunction, otherFunction])
);
});
});
});
191 changes: 172 additions & 19 deletions x-pack/plugins/canvas/server/collectors/workpad_collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { sum as arraySum, min as arrayMin, max as arrayMax, get } from 'lodash';
import moment from 'moment';
import { MakeSchemaFrom } from 'src/plugins/usage_collection/server';
import { CANVAS_TYPE } from '../../common/lib/constants';
import { collectFns } from './collector_helpers';
Expand Down Expand Up @@ -39,6 +40,8 @@ export interface WorkpadTelemetry {
functions?: {
total: number;
in_use: string[];
in_use_30d: string[];
in_use_90d: string[];
per_element: {
avg: number;
min: number;
Expand All @@ -56,38 +59,156 @@ export interface WorkpadTelemetry {
}

export const workpadSchema: MakeSchemaFrom<WorkpadTelemetry> = {
workpads: { total: { type: 'long' } },
workpads: {
total: {
type: 'long',
_meta: {
description: 'The total number of Canvas Workpads in the cluster',
},
},
},
pages: {
total: { type: 'long' },
total: {
type: 'long',
_meta: {
description: 'The total number of pages across all Canvas Workpads',
},
},
per_workpad: {
avg: { type: 'float' },
min: { type: 'long' },
max: { type: 'long' },
avg: {
type: 'float',
_meta: {
description: 'The average number of pages across all Canvas Workpads',
},
},
min: {
type: 'long',
_meta: {
description: 'The minimum number of pages found in a Canvas Workpad',
},
},
max: {
type: 'long',
_meta: {
description: 'The maximum number of pages found in a Canvas Workpad',
},
},
},
},
elements: {
total: { type: 'long' },
total: {
type: 'long',
_meta: {
description: 'The total number of elements across all Canvas Workpads',
},
},
per_page: {
avg: { type: 'float' },
min: { type: 'long' },
max: { type: 'long' },
avg: {
type: 'float',
_meta: {
description: 'The average number of elements per page across all Canvas Workpads',
},
},
min: {
type: 'long',
_meta: {
description: 'The minimum number of elements on a page across all Canvas Workpads',
},
},
max: {
type: 'long',
_meta: {
description: 'The maximum number of elements on a page across all Canvas Workpads',
},
},
},
},
functions: {
total: { type: 'long' },
in_use: { type: 'array', items: { type: 'keyword' } },
total: {
type: 'long',
_meta: {
description: 'The total number of functions in use across all Canvas Workpads',
},
},
in_use: {
type: 'array',
items: {
type: 'keyword',
_meta: {
description: 'A function in use in any Canvas Workpad',
},
},
},
in_use_30d: {
type: 'array',
items: {
type: 'keyword',
_meta: {
description:
'A function in use in a Canvas Workpad that has been modified in the last 30 days',
},
},
},
in_use_90d: {
type: 'array',
items: {
type: 'keyword',
_meta: {
description:
'A function in use in a Canvas Workpad that has been modified in the last 90 days',
},
},
},
per_element: {
avg: { type: 'float' },
min: { type: 'long' },
max: { type: 'long' },
avg: {
type: 'float',
_meta: {
description: 'Average number of functions used per element across all Canvas Workpads',
},
},
min: {
type: 'long',
_meta: {
description:
'The minimum number of functions used in an element across all Canvas Workpads',
},
},
max: {
type: 'long',
_meta: {
description:
'The maximum number of functions used in an element across all Canvas Workpads',
},
},
},
},
variables: {
total: { type: 'long' },
total: {
type: 'long',
_meta: {
description: 'The total number of variables defined across all Canvas Workpads',
},
},

per_workpad: {
avg: { type: 'float' },
min: { type: 'long' },
max: { type: 'long' },
avg: {
type: 'float',
_meta: {
description: 'The average number of variables set per Canvas Workpad',
},
},
min: {
type: 'long',
_meta: {
description: 'The minimum number variables set across all Canvas Workpads',
},
},
max: {
type: 'long',
_meta: {
description: 'The maximum number of variables set across all Canvas Workpads',
},
},
},
},
};
Expand All @@ -98,6 +219,11 @@ export const workpadSchema: MakeSchemaFrom<WorkpadTelemetry> = {
@returns Workpad Telemetry Data
*/
export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetry {
const functionCollection = {
all: new Set<string>(),
'30d': new Set<string>(),
'90d': new Set<string>(),
};
const functionSet = new Set<string>();

if (workpadDocs.length === 0) {
Expand All @@ -106,6 +232,21 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr

// make a summary of info about each workpad
const workpadsInfo = workpadDocs.map((workpad) => {
let this30Days = false;
let this90Days = false;

if (workpad['@timestamp'] !== undefined) {
const lastReadDaysAgo = moment().diff(moment(workpad['@timestamp']), 'days');

if (lastReadDaysAgo < 30) {
this30Days = true;
}

if (lastReadDaysAgo < 90) {
this90Days = true;
}
}

let pages = { count: 0 };
try {
pages = { count: workpad.pages.length };
Expand All @@ -121,6 +262,16 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr
return page.elements.map((element) => {
const ast = parseExpression(element.expression);
collectFns(ast, (cFunction) => {
functionCollection.all.add(cFunction);

if (this30Days) {
functionCollection['30d'].add(cFunction);
}

if (this90Days) {
functionCollection['90d'].add(cFunction);
}

functionSet.add(cFunction);
});
return ast.chain.length; // get the number of parts in the expression
Expand Down Expand Up @@ -203,7 +354,9 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr
elementsTotal > 0
? {
total: functionsTotal,
in_use: Array.from(functionSet),
in_use: Array.from(functionCollection.all),
in_use_30d: Array.from(functionCollection['30d']),
in_use_90d: Array.from(functionCollection['90d']),
per_element: {
avg: functionsTotal / functionCounts.length,
min: arrayMin(functionCounts) || 0,
Expand Down
Loading