mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Cases] Adding mime type telemetry (#154679)
This PR adds telemetry for collecting statistics for the mimetypes of the files uploaded to cases. It only looks for the top 20 mime types using a terms aggregation. It does this across all cases and broken down by owner. On the telemetry server we'll need to create a single document per entry in the array here so we can do filtering for the visualizations. Example ``` "topMimeTypes": [ { "count": 2, "name": "image/png" }, { "count": 1, "name": "application/json" }, { "count": 1, "name": "text/plain" } ] ``` <details><summary>Example telemetry result</summary> "cases": { "cases": { "all": { "total": 1, "daily": 1, "weekly": 1, "monthly": 1, "status": { "open": 1, "inProgress": 0, "closed": 0 }, "syncAlertsOn": 1, "syncAlertsOff": 0, "totalUsers": 1, "totalParticipants": 1, "totalTags": 1, "totalWithAlerts": 0, "totalWithConnectors": 0, "latestDates": { "createdAt": "2023-04-11T16:07:00.565Z", "updatedAt": "2023-04-11T16:07:17.712Z", "closedAt": "" }, "assignees": { "total": 0, "totalWithZero": 1, "totalWithAtLeastOne": 0 }, "attachmentFramework": { "externalAttachments": [ { "type": ".files", "average": 4, "maxOnACase": 4, "total": 4 } ], "persistableAttachments": [], "files": { "averageSize": 3, "average": 4, "maxOnACase": 4, "total": 4, "topMimeTypes": [ { "count": 2, "name": "image/png" }, { "count": 1, "name": "application/json" }, { "count": 1, "name": "text/plain" } ] } } }, "sec": { "total": 0, "daily": 0, "weekly": 0, "monthly": 0, "attachmentFramework": { "externalAttachments": [], "persistableAttachments": [], "files": { "averageSize": 0, "average": 0, "maxOnACase": 0, "total": 0, "topMimeTypes": [] } }, "assignees": { "total": 0, "totalWithZero": 0, "totalWithAtLeastOne": 0 } }, "obs": { "total": 0, "daily": 0, "weekly": 0, "monthly": 0, "attachmentFramework": { "externalAttachments": [], "persistableAttachments": [], "files": { "averageSize": 0, "average": 0, "maxOnACase": 0, "total": 0, "topMimeTypes": [] } }, "assignees": { "total": 0, "totalWithZero": 0, "totalWithAtLeastOne": 0 } }, "main": { "total": 1, "daily": 1, "weekly": 1, "monthly": 1, "attachmentFramework": { "externalAttachments": [ { "type": ".files", "average": 4, "maxOnACase": 4, "total": 4 } ], "persistableAttachments": [], "files": { "averageSize": 3, "average": 4, "maxOnACase": 4, "total": 4, "topMimeTypes": [ { "count": 2, "name": "image/png" }, { "count": 1, "name": "application/json" }, { "count": 1, "name": "text/plain" } ] } }, "assignees": { "total": 0, "totalWithZero": 1, "totalWithAtLeastOne": 0 } } }, "userActions": { "all": { "total": 5, "daily": 5, "weekly": 5, "monthly": 5, "maxOnACase": 5 } }, "comments": { "all": { "total": 0, "daily": 0, "weekly": 0, "monthly": 0, "maxOnACase": 0 } }, "alerts": { "all": { "total": 0, "daily": 0, "weekly": 0, "monthly": 0, "maxOnACase": 0 } }, "connectors": { "all": { "all": { "totalAttached": 0 }, "itsm": { "totalAttached": 0 }, "sir": { "totalAttached": 0 }, "jira": { "totalAttached": 0 }, "resilient": { "totalAttached": 0 }, "swimlane": { "totalAttached": 0 }, "maxAttachedToACase": 0 } }, "pushes": { "all": { "total": 0, "maxOnACase": 0 } }, "configuration": { "all": { "closure": { "manually": 0, "automatic": 0 } } } } </details> ## Testing To test modify this file: https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/server/telemetry/schedule_telemetry_task.ts With: ``` export const scheduleCasesTelemetryTask = ( taskManager: TaskManagerStartContract, logger: Logger ) => { (async () => { await taskManager .ensureScheduled({ id: CASES_TELEMETRY_TASK_NAME, taskType: CASES_TELEMETRY_TASK_NAME, schedule: { interval: `${MINUTES_ON_HALF_DAY}m`, }, scope: ['cases'], params: {}, state: {}, }) .catch((err) => { logger.debug( `Error scheduling cases task with ID ${CASES_TELEMETRY_TASK_NAME} and type ${CASES_TELEMETRY_TASK_NAME}. Received ${err.message}` ); }); await taskManager.runSoon(CASES_TELEMETRY_TASK_NAME); })(); }; ``` This will cause the telemetry to be sent as soon as the server is restarted. To generate files and attachments to add stats to the telemetry I created this python script: https://github.com/elastic/cases-files-generator To retrieve the telemetry: ``` POST http://localhost:5601/api/telemetry/v2/clusters/_stats { "refreshCache": true, "unencrypted": true } ```
This commit is contained in:
parent
07a7e765f6
commit
fe9985b566
7 changed files with 385 additions and 45 deletions
|
@ -12,7 +12,7 @@ import type {
|
||||||
AttachmentAggregationResult,
|
AttachmentAggregationResult,
|
||||||
AttachmentFrameworkAggsResult,
|
AttachmentFrameworkAggsResult,
|
||||||
CaseAggregationResult,
|
CaseAggregationResult,
|
||||||
FileAttachmentAggregationResult,
|
FileAttachmentAggregationResults,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { getCasesTelemetryData } from './cases';
|
import { getCasesTelemetryData } from './cases';
|
||||||
|
|
||||||
|
@ -187,17 +187,65 @@ describe('getCasesTelemetryData', () => {
|
||||||
...attachmentFramework,
|
...attachmentFramework,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filesRes: FileAttachmentAggregationResult = {
|
const filesRes: FileAttachmentAggregationResults = {
|
||||||
securitySolution: {
|
securitySolution: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
observability: {
|
observability: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cases: {
|
cases: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
averageSize: 500,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mockFind(caseAggsResult);
|
mockFind(caseAggsResult);
|
||||||
|
@ -259,6 +307,16 @@ describe('getCasesTelemetryData', () => {
|
||||||
average,
|
average,
|
||||||
maxOnACase: 10,
|
maxOnACase: 10,
|
||||||
total,
|
total,
|
||||||
|
topMimeTypes: [
|
||||||
|
{
|
||||||
|
count: 5,
|
||||||
|
name: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 1,
|
||||||
|
name: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -644,6 +702,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"persistableReferenceTypes": Object {
|
"persistableReferenceTypes": Object {
|
||||||
|
@ -677,6 +736,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -717,6 +777,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"observability": Object {
|
"observability": Object {
|
||||||
|
@ -752,6 +813,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"persistableReferenceTypes": Object {
|
"persistableReferenceTypes": Object {
|
||||||
|
@ -785,6 +847,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -830,6 +893,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"securitySolution": Object {
|
"securitySolution": Object {
|
||||||
|
@ -865,6 +929,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
"field": "cases-comments.attributes.externalReferenceAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"persistableReferenceTypes": Object {
|
"persistableReferenceTypes": Object {
|
||||||
|
@ -898,6 +963,7 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
"terms": Object {
|
"terms": Object {
|
||||||
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
"field": "cases-comments.attributes.persistableStateAttachmentTypeId",
|
||||||
|
"size": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1031,6 +1097,12 @@ describe('getCasesTelemetryData', () => {
|
||||||
"field": "file.attributes.size",
|
"field": "file.attributes.size",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"topMimeTypes": Object {
|
||||||
|
"terms": Object {
|
||||||
|
"field": "file.attributes.mime_type",
|
||||||
|
"size": 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"filter": Object {
|
"filter": Object {
|
||||||
"term": Object {
|
"term": Object {
|
||||||
|
@ -1045,6 +1117,12 @@ describe('getCasesTelemetryData', () => {
|
||||||
"field": "file.attributes.size",
|
"field": "file.attributes.size",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"topMimeTypes": Object {
|
||||||
|
"terms": Object {
|
||||||
|
"field": "file.attributes.mime_type",
|
||||||
|
"size": 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"filter": Object {
|
"filter": Object {
|
||||||
"term": Object {
|
"term": Object {
|
||||||
|
@ -1059,6 +1137,12 @@ describe('getCasesTelemetryData', () => {
|
||||||
"field": "file.attributes.size",
|
"field": "file.attributes.size",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"topMimeTypes": Object {
|
||||||
|
"terms": Object {
|
||||||
|
"field": "file.attributes.mime_type",
|
||||||
|
"size": 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"filter": Object {
|
"filter": Object {
|
||||||
"term": Object {
|
"term": Object {
|
||||||
|
@ -1066,13 +1150,19 @@ describe('getCasesTelemetryData', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"topMimeTypes": Object {
|
||||||
|
"terms": Object {
|
||||||
|
"field": "file.attributes.mime_type",
|
||||||
|
"size": 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"filter": Object {
|
"filter": Object {
|
||||||
"arguments": Array [
|
"arguments": Array [
|
||||||
Object {
|
Object {
|
||||||
"isQuoted": false,
|
"isQuoted": false,
|
||||||
"type": "literal",
|
"type": "literal",
|
||||||
"value": "file.attributes.Meta.caseId",
|
"value": "file.attributes.Meta.caseIds",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"type": "wildcard",
|
"type": "wildcard",
|
||||||
|
|
|
@ -23,7 +23,7 @@ import type {
|
||||||
LatestDates,
|
LatestDates,
|
||||||
CaseAggregationResult,
|
CaseAggregationResult,
|
||||||
AttachmentAggregationResult,
|
AttachmentAggregationResult,
|
||||||
FileAttachmentAggregationResult,
|
FileAttachmentAggregationResults,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
findValueInBuckets,
|
findValueInBuckets,
|
||||||
|
@ -234,6 +234,7 @@ const getCommentsSavedObjectTelemetry = async (
|
||||||
externalReferenceTypes: {
|
externalReferenceTypes: {
|
||||||
terms: {
|
terms: {
|
||||||
field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.externalReferenceAttachmentTypeId`,
|
field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.externalReferenceAttachmentTypeId`,
|
||||||
|
size: 10,
|
||||||
},
|
},
|
||||||
aggs: {
|
aggs: {
|
||||||
...getMaxBucketOnCaseAggregationQuery(CASE_COMMENT_SAVED_OBJECT),
|
...getMaxBucketOnCaseAggregationQuery(CASE_COMMENT_SAVED_OBJECT),
|
||||||
|
@ -242,6 +243,7 @@ const getCommentsSavedObjectTelemetry = async (
|
||||||
persistableReferenceTypes: {
|
persistableReferenceTypes: {
|
||||||
terms: {
|
terms: {
|
||||||
field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.persistableStateAttachmentTypeId`,
|
field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.persistableStateAttachmentTypeId`,
|
||||||
|
size: 10,
|
||||||
},
|
},
|
||||||
aggs: {
|
aggs: {
|
||||||
...getMaxBucketOnCaseAggregationQuery(CASE_COMMENT_SAVED_OBJECT),
|
...getMaxBucketOnCaseAggregationQuery(CASE_COMMENT_SAVED_OBJECT),
|
||||||
|
@ -284,7 +286,7 @@ const getCommentsSavedObjectTelemetry = async (
|
||||||
|
|
||||||
const getFilesTelemetry = async (
|
const getFilesTelemetry = async (
|
||||||
savedObjectsClient: ISavedObjectsRepository
|
savedObjectsClient: ISavedObjectsRepository
|
||||||
): Promise<SavedObjectsFindResponse<unknown, FileAttachmentAggregationResult>> => {
|
): Promise<SavedObjectsFindResponse<unknown, FileAttachmentAggregationResults>> => {
|
||||||
const averageSize = () => ({
|
const averageSize = () => ({
|
||||||
averageSize: {
|
averageSize: {
|
||||||
avg: {
|
avg: {
|
||||||
|
@ -293,6 +295,15 @@ const getFilesTelemetry = async (
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const top20MimeTypes = () => ({
|
||||||
|
topMimeTypes: {
|
||||||
|
terms: {
|
||||||
|
field: `${FILE_SO_TYPE}.attributes.mime_type`,
|
||||||
|
size: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const filesByOwnerAggregationQuery = OWNERS.reduce(
|
const filesByOwnerAggregationQuery = OWNERS.reduce(
|
||||||
(aggQuery, owner) => ({
|
(aggQuery, owner) => ({
|
||||||
...aggQuery,
|
...aggQuery,
|
||||||
|
@ -304,20 +315,21 @@ const getFilesTelemetry = async (
|
||||||
},
|
},
|
||||||
aggs: {
|
aggs: {
|
||||||
...averageSize(),
|
...averageSize(),
|
||||||
|
...top20MimeTypes(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterCaseIdExists = fromKueryExpression(`${FILE_SO_TYPE}.attributes.Meta.caseId: *`);
|
const filterCaseIdExists = fromKueryExpression(`${FILE_SO_TYPE}.attributes.Meta.caseIds: *`);
|
||||||
|
|
||||||
return savedObjectsClient.find<unknown, FileAttachmentAggregationResult>({
|
return savedObjectsClient.find<unknown, FileAttachmentAggregationResults>({
|
||||||
page: 0,
|
page: 0,
|
||||||
perPage: 0,
|
perPage: 0,
|
||||||
type: FILE_SO_TYPE,
|
type: FILE_SO_TYPE,
|
||||||
filter: filterCaseIdExists,
|
filter: filterCaseIdExists,
|
||||||
aggs: { ...filesByOwnerAggregationQuery, ...averageSize() },
|
aggs: { ...filesByOwnerAggregationQuery, ...averageSize(), ...top20MimeTypes() },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type {
|
||||||
AttachmentAggregationResult,
|
AttachmentAggregationResult,
|
||||||
AttachmentFrameworkAggsResult,
|
AttachmentFrameworkAggsResult,
|
||||||
CaseAggregationResult,
|
CaseAggregationResult,
|
||||||
FileAttachmentAggregationResult,
|
FileAttachmentAggregationResults,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
findValueInBuckets,
|
findValueInBuckets,
|
||||||
|
@ -168,17 +168,65 @@ describe('utils', () => {
|
||||||
...attachmentFramework,
|
...attachmentFramework,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filesRes: FileAttachmentAggregationResult = {
|
const filesRes: FileAttachmentAggregationResults = {
|
||||||
securitySolution: {
|
securitySolution: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
observability: {
|
observability: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cases: {
|
cases: {
|
||||||
averageSize: 500,
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
averageSize: 500,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it('constructs the solution values correctly', () => {
|
it('constructs the solution values correctly', () => {
|
||||||
|
@ -215,6 +263,16 @@ describe('utils', () => {
|
||||||
"average": 1,
|
"average": 1,
|
||||||
"averageSize": 500,
|
"averageSize": 500,
|
||||||
"maxOnACase": 10,
|
"maxOnACase": 10,
|
||||||
|
"topMimeTypes": Array [
|
||||||
|
Object {
|
||||||
|
"count": 5,
|
||||||
|
"name": "image/png",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": 1,
|
||||||
|
"name": "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
"total": 5,
|
"total": 5,
|
||||||
},
|
},
|
||||||
"persistableAttachments": Array [
|
"persistableAttachments": Array [
|
||||||
|
@ -271,6 +329,16 @@ describe('utils', () => {
|
||||||
"average": 5,
|
"average": 5,
|
||||||
"averageSize": 500,
|
"averageSize": 500,
|
||||||
"maxOnACase": 10,
|
"maxOnACase": 10,
|
||||||
|
"topMimeTypes": Array [
|
||||||
|
Object {
|
||||||
|
"count": 5,
|
||||||
|
"name": "image/png",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": 1,
|
||||||
|
"name": "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
"total": 5,
|
"total": 5,
|
||||||
},
|
},
|
||||||
"persistableAttachments": Array [
|
"persistableAttachments": Array [
|
||||||
|
@ -327,6 +395,16 @@ describe('utils', () => {
|
||||||
"average": 5,
|
"average": 5,
|
||||||
"averageSize": 500,
|
"averageSize": 500,
|
||||||
"maxOnACase": 10,
|
"maxOnACase": 10,
|
||||||
|
"topMimeTypes": Array [
|
||||||
|
Object {
|
||||||
|
"count": 5,
|
||||||
|
"name": "image/png",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": 1,
|
||||||
|
"name": "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
"total": 5,
|
"total": 5,
|
||||||
},
|
},
|
||||||
"persistableAttachments": Array [
|
"persistableAttachments": Array [
|
||||||
|
@ -363,6 +441,7 @@ describe('utils', () => {
|
||||||
"average": 0,
|
"average": 0,
|
||||||
"averageSize": 0,
|
"averageSize": 0,
|
||||||
"maxOnACase": 0,
|
"maxOnACase": 0,
|
||||||
|
"topMimeTypes": Array [],
|
||||||
"total": 0,
|
"total": 0,
|
||||||
},
|
},
|
||||||
"persistableAttachments": Array [],
|
"persistableAttachments": Array [],
|
||||||
|
@ -486,7 +565,7 @@ describe('utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('files', () => {
|
describe('files', () => {
|
||||||
it('sets the files stats to empty when it cannot find a files entry', () => {
|
it('sets the files stats to empty when the file aggregation results is the empty version', () => {
|
||||||
const attachmentFramework: AttachmentFrameworkAggsResult = {
|
const attachmentFramework: AttachmentFrameworkAggsResult = {
|
||||||
externalReferenceTypes: {
|
externalReferenceTypes: {
|
||||||
buckets: [
|
buckets: [
|
||||||
|
@ -512,19 +591,25 @@ describe('utils', () => {
|
||||||
getAttachmentsFrameworkStats({
|
getAttachmentsFrameworkStats({
|
||||||
attachmentAggregations: attachmentFramework,
|
attachmentAggregations: attachmentFramework,
|
||||||
totalCasesForOwner: 5,
|
totalCasesForOwner: 5,
|
||||||
filesAggregations: { averageSize: 500 },
|
filesAggregations: {
|
||||||
|
averageSize: { value: 0 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
}).attachmentFramework.files
|
}).attachmentFramework.files
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"average": 0,
|
"average": 0,
|
||||||
"averageSize": 0,
|
"averageSize": 0,
|
||||||
"maxOnACase": 0,
|
"maxOnACase": 0,
|
||||||
|
"topMimeTypes": Array [],
|
||||||
"total": 0,
|
"total": 0,
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the files stats when it finds a files entry', () => {
|
it('sets the files stats using the file aggregation result', () => {
|
||||||
const attachmentFramework: AttachmentFrameworkAggsResult = {
|
const attachmentFramework: AttachmentFrameworkAggsResult = {
|
||||||
externalReferenceTypes: {
|
externalReferenceTypes: {
|
||||||
buckets: [
|
buckets: [
|
||||||
|
@ -549,7 +634,21 @@ describe('utils', () => {
|
||||||
expect(
|
expect(
|
||||||
getAttachmentsFrameworkStats({
|
getAttachmentsFrameworkStats({
|
||||||
attachmentAggregations: attachmentFramework,
|
attachmentAggregations: attachmentFramework,
|
||||||
filesAggregations: { averageSize: 500 },
|
filesAggregations: {
|
||||||
|
averageSize: { value: 500 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
totalCasesForOwner: 5,
|
totalCasesForOwner: 5,
|
||||||
}).attachmentFramework.files
|
}).attachmentFramework.files
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
|
@ -557,10 +656,70 @@ describe('utils', () => {
|
||||||
"average": 1,
|
"average": 1,
|
||||||
"averageSize": 500,
|
"averageSize": 500,
|
||||||
"maxOnACase": 10,
|
"maxOnACase": 10,
|
||||||
|
"topMimeTypes": Array [
|
||||||
|
Object {
|
||||||
|
"count": 5,
|
||||||
|
"name": "image/png",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": 1,
|
||||||
|
"name": "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
"total": 5,
|
"total": 5,
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the top mime types when a file entry is not found', () => {
|
||||||
|
const attachmentFramework: AttachmentFrameworkAggsResult = {
|
||||||
|
externalReferenceTypes: {
|
||||||
|
buckets: [],
|
||||||
|
},
|
||||||
|
persistableReferenceTypes: {
|
||||||
|
buckets: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getAttachmentsFrameworkStats({
|
||||||
|
attachmentAggregations: attachmentFramework,
|
||||||
|
filesAggregations: {
|
||||||
|
averageSize: { value: 0 },
|
||||||
|
topMimeTypes: {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
doc_count: 5,
|
||||||
|
key: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc_count: 1,
|
||||||
|
key: 'application/json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
totalCasesForOwner: 5,
|
||||||
|
}).attachmentFramework.files
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"average": 0,
|
||||||
|
"averageSize": 0,
|
||||||
|
"maxOnACase": 0,
|
||||||
|
"topMimeTypes": Array [
|
||||||
|
Object {
|
||||||
|
"count": 5,
|
||||||
|
"name": "image/png",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": 1,
|
||||||
|
"name": "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total": 0,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ import type {
|
||||||
BucketsWithMaxOnCase,
|
BucketsWithMaxOnCase,
|
||||||
AttachmentStats,
|
AttachmentStats,
|
||||||
FileAttachmentStats,
|
FileAttachmentStats,
|
||||||
FileAttachmentAggregationResult,
|
FileAttachmentAggregationResults,
|
||||||
FileAttachmentAverageSize,
|
FileAttachmentAggsResult,
|
||||||
AttachmentFrameworkAggsResult,
|
AttachmentFrameworkAggsResult,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { buildFilter } from '../../client/utils';
|
import { buildFilter } from '../../client/utils';
|
||||||
|
@ -170,7 +170,7 @@ export const getSolutionValues = ({
|
||||||
}: {
|
}: {
|
||||||
caseAggregations?: CaseAggregationResult;
|
caseAggregations?: CaseAggregationResult;
|
||||||
attachmentAggregations?: AttachmentAggregationResult;
|
attachmentAggregations?: AttachmentAggregationResult;
|
||||||
filesAggregations?: FileAttachmentAggregationResult;
|
filesAggregations?: FileAttachmentAggregationResults;
|
||||||
owner: Owner;
|
owner: Owner;
|
||||||
}): SolutionTelemetry => {
|
}): SolutionTelemetry => {
|
||||||
const aggregationsBuckets = getAggregationsBuckets({
|
const aggregationsBuckets = getAggregationsBuckets({
|
||||||
|
@ -223,13 +223,15 @@ export const getAttachmentsFrameworkStats = ({
|
||||||
totalCasesForOwner,
|
totalCasesForOwner,
|
||||||
}: {
|
}: {
|
||||||
attachmentAggregations?: AttachmentFrameworkAggsResult;
|
attachmentAggregations?: AttachmentFrameworkAggsResult;
|
||||||
filesAggregations?: FileAttachmentAverageSize;
|
filesAggregations?: FileAttachmentAggsResult;
|
||||||
totalCasesForOwner: number;
|
totalCasesForOwner: number;
|
||||||
}): AttachmentFramework => {
|
}): AttachmentFramework => {
|
||||||
if (!attachmentAggregations) {
|
if (!attachmentAggregations) {
|
||||||
return emptyAttachmentFramework();
|
return emptyAttachmentFramework();
|
||||||
}
|
}
|
||||||
const averageFileSize = filesAggregations?.averageSize;
|
|
||||||
|
const averageFileSize = filesAggregations?.averageSize?.value;
|
||||||
|
const topMimeTypes = filesAggregations?.topMimeTypes;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachmentFramework: {
|
attachmentFramework: {
|
||||||
|
@ -245,6 +247,7 @@ export const getAttachmentsFrameworkStats = ({
|
||||||
registryResults: attachmentAggregations.externalReferenceTypes,
|
registryResults: attachmentAggregations.externalReferenceTypes,
|
||||||
averageFileSize,
|
averageFileSize,
|
||||||
totalCasesForOwner,
|
totalCasesForOwner,
|
||||||
|
topMimeTypes,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -272,8 +275,8 @@ const getAttachmentRegistryStats = (
|
||||||
return stats;
|
return stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateTypePerCaseAverage = (typeDocCount: number, totalCases: number) => {
|
const calculateTypePerCaseAverage = (typeDocCount: number | undefined, totalCases: number) => {
|
||||||
if (totalCases === 0) {
|
if (typeDocCount == null || totalCases === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,22 +287,27 @@ const getFileAttachmentStats = ({
|
||||||
registryResults,
|
registryResults,
|
||||||
averageFileSize,
|
averageFileSize,
|
||||||
totalCasesForOwner,
|
totalCasesForOwner,
|
||||||
|
topMimeTypes,
|
||||||
}: {
|
}: {
|
||||||
registryResults: BucketsWithMaxOnCase;
|
registryResults: BucketsWithMaxOnCase;
|
||||||
averageFileSize?: number;
|
averageFileSize?: number;
|
||||||
totalCasesForOwner: number;
|
totalCasesForOwner: number;
|
||||||
|
topMimeTypes?: Buckets<string>;
|
||||||
}): FileAttachmentStats => {
|
}): FileAttachmentStats => {
|
||||||
const fileBucket = registryResults.buckets.find((bucket) => bucket.key === FILE_ATTACHMENT_TYPE);
|
const fileBucket = registryResults.buckets.find((bucket) => bucket.key === FILE_ATTACHMENT_TYPE);
|
||||||
|
|
||||||
if (!fileBucket || averageFileSize == null) {
|
const mimeTypes =
|
||||||
return emptyFileAttachment();
|
topMimeTypes?.buckets.map((mimeType) => ({
|
||||||
}
|
count: mimeType.doc_count,
|
||||||
|
name: mimeType.key,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
averageSize: averageFileSize,
|
averageSize: averageFileSize ?? 0,
|
||||||
average: calculateTypePerCaseAverage(fileBucket.doc_count, totalCasesForOwner),
|
average: calculateTypePerCaseAverage(fileBucket?.doc_count, totalCasesForOwner),
|
||||||
maxOnACase: fileBucket.references.cases.max.value,
|
maxOnACase: fileBucket?.references.cases.max.value ?? 0,
|
||||||
total: fileBucket.doc_count,
|
total: fileBucket?.doc_count ?? 0,
|
||||||
|
topMimeTypes: mimeTypes,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -332,4 +340,5 @@ const emptyFileAttachment = (): FileAttachmentStats => ({
|
||||||
averageSize: 0,
|
averageSize: 0,
|
||||||
maxOnACase: 0,
|
maxOnACase: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
topMimeTypes: [],
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,6 +51,13 @@ const attachmentFrameworkSchema: AttachmentFrameworkSchema = {
|
||||||
averageSize: long,
|
averageSize: long,
|
||||||
maxOnACase: long,
|
maxOnACase: long,
|
||||||
total: long,
|
total: long,
|
||||||
|
topMimeTypes: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
count: long,
|
||||||
|
name: string,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,15 @@ import type { ISavedObjectsRepository, Logger } from '@kbn/core/server';
|
||||||
import type { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server';
|
import type { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server';
|
||||||
import type { Owner } from '../../common/constants/types';
|
import type { Owner } from '../../common/constants/types';
|
||||||
|
|
||||||
export interface Buckets {
|
export type BucketKeyString = Omit<Bucket, 'key'> & { key: string };
|
||||||
buckets: Array<{
|
|
||||||
doc_count: number;
|
interface Bucket<T extends string | number = string | number> {
|
||||||
key: number | string;
|
doc_count: number;
|
||||||
}>;
|
key: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Buckets<T extends string | number = string | number> {
|
||||||
|
buckets: Array<Bucket<T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Cardinality {
|
export interface Cardinality {
|
||||||
|
@ -57,12 +61,15 @@ export interface AssigneesFilters {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileAttachmentAverageSize {
|
export interface FileAttachmentAggsResult {
|
||||||
averageSize: number;
|
averageSize: {
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
topMimeTypes: Buckets<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FileAttachmentAggregationResult = Record<Owner, FileAttachmentAverageSize> &
|
export type FileAttachmentAggregationResults = Record<Owner, FileAttachmentAggsResult> &
|
||||||
FileAttachmentAverageSize;
|
FileAttachmentAggsResult;
|
||||||
|
|
||||||
export interface BucketsWithMaxOnCase {
|
export interface BucketsWithMaxOnCase {
|
||||||
buckets: Array<
|
buckets: Array<
|
||||||
|
@ -118,6 +125,10 @@ export interface AttachmentStats extends CommonAttachmentStats {
|
||||||
|
|
||||||
export interface FileAttachmentStats extends CommonAttachmentStats {
|
export interface FileAttachmentStats extends CommonAttachmentStats {
|
||||||
averageSize: number;
|
averageSize: number;
|
||||||
|
topMimeTypes: Array<{
|
||||||
|
name: string;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttachmentFramework {
|
export interface AttachmentFramework {
|
||||||
|
|
|
@ -4839,6 +4839,19 @@
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"type": "long"
|
"type": "long"
|
||||||
|
},
|
||||||
|
"topMimeTypes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4986,6 +4999,19 @@
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"type": "long"
|
"type": "long"
|
||||||
|
},
|
||||||
|
"topMimeTypes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5073,6 +5099,19 @@
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"type": "long"
|
"type": "long"
|
||||||
|
},
|
||||||
|
"topMimeTypes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5160,6 +5199,19 @@
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"type": "long"
|
"type": "long"
|
||||||
|
},
|
||||||
|
"topMimeTypes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue