Skip to content

Commit

Permalink
[Telemetry] collect number of visualization saved in the past 7, 30 a…
Browse files Browse the repository at this point in the history
…nd 90 days (elastic#67865)

* Update telemetry for visualizations to also count the vis from the past 30 and 90 days

* Also add metrics for the saved visualizations for the past 7 days

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
stratoula and elasticmachine committed Jun 8, 2020
1 parent 823ce64 commit 3bbfd11
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 14 deletions.
22 changes: 22 additions & 0 deletions x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import moment from 'moment';
import { getPastDays } from './get_past_days';

describe('getPastDays', () => {
test('Returns 2 days that have passed from the current date', () => {
const pastDate = moment().subtract(2, 'days').startOf('day').toString();

expect(getPastDays(pastDate)).toEqual(2);
});

test('Returns 30 days that have passed from the current date', () => {
const pastDate = moment().subtract(30, 'days').startOf('day').toString();

expect(getPastDays(pastDate)).toEqual(30);
});
});
11 changes: 11 additions & 0 deletions x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const getPastDays = (dateString: string): number => {
const date = new Date(dateString);
const today = new Date();
const diff = Math.abs(date.getTime() - today.getTime());
return Math.trunc(diff / (1000 * 60 * 60 * 24));
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { visualizationsTaskRunner } from './task_runner';
import { TaskInstance } from '../../../../../task_manager/server';
import { getNextMidnight } from '../../get_next_midnight';
import moment from 'moment';

describe('visualizationsTaskRunner', () => {
let mockTaskInstance: TaskInstance;
Expand Down Expand Up @@ -55,6 +56,9 @@ describe('visualizationsTaskRunner', () => {
spaces_max: 1,
spaces_min: 1,
total: 1,
saved_7_days_total: 1,
saved_30_days_total: 1,
saved_90_days_total: 1,
},
},
},
Expand All @@ -69,57 +73,124 @@ describe('visualizationsTaskRunner', () => {
_source: {
type: 'visualization',
visualization: { visState: '{"type": "cave_painting"}' },
updated_at: moment().subtract(7, 'days').startOf('day').toString(),
},
},
{
_id: 'visualization:coolviz-456',
_source: {
type: 'visualization',
visualization: { visState: '{"type": "printing_press"}' },
updated_at: moment().subtract(20, 'days').startOf('day').toString(),
},
},
{
_id: 'meat:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "floppy_disk"}' },
updated_at: moment().subtract(2, 'months').startOf('day').toString(),
},
},
// meat space
{
_id: 'meat:visualization:coolviz-789',
_source: {
type: 'visualization',
visualization: { visState: '{"type": "cave_painting"}' },
updated_at: moment().subtract(89, 'days').startOf('day').toString(),
},
},
{
_id: 'meat:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "cuneiform"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "cuneiform"}' },
updated_at: moment().subtract(5, 'months').startOf('day').toString(),
},
},
{
_id: 'meat:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "cuneiform"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "cuneiform"}' },
updated_at: moment().subtract(2, 'days').startOf('day').toString(),
},
},
{
_id: 'meat:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "floppy_disk"}' },
updated_at: moment().subtract(7, 'days').startOf('day').toString(),
},
},
// cyber space
{
_id: 'cyber:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "floppy_disk"}' },
updated_at: moment().subtract(7, 'months').startOf('day').toString(),
},
},
{
_id: 'cyber:visualization:coolviz-789',
_source: { type: 'visualization', visualization: { visState: '{"type": "floppy_disk"}' } },
_source: {
type: 'visualization',
visualization: { visState: '{"type": "floppy_disk"}' },
updated_at: moment().subtract(3, 'days').startOf('day').toString(),
},
},
{
_id: 'cyber:visualization:coolviz-123',
_source: {
type: 'visualization',
visualization: { visState: '{"type": "cave_painting"}' },
updated_at: moment().subtract(15, 'days').startOf('day').toString(),
},
},
]);

const expectedStats = {
cave_painting: {
total: 3,
spaces_min: 1,
spaces_max: 1,
spaces_avg: 1,
saved_7_days_total: 1,
saved_30_days_total: 2,
saved_90_days_total: 3,
},
printing_press: {
total: 1,
spaces_min: 1,
spaces_max: 1,
spaces_avg: 1,
saved_7_days_total: 0,
saved_30_days_total: 1,
saved_90_days_total: 1,
},
cuneiform: {
total: 2,
spaces_min: 2,
spaces_max: 2,
spaces_avg: 2,
saved_7_days_total: 1,
saved_30_days_total: 1,
saved_90_days_total: 1,
},
floppy_disk: {
total: 4,
spaces_min: 2,
spaces_max: 2,
spaces_avg: 2,
saved_7_days_total: 2,
saved_30_days_total: 2,
saved_90_days_total: 3,
},
};

const runner = visualizationsTaskRunner(
mockTaskInstance,
getMockConfig(),
Expand All @@ -131,13 +202,10 @@ describe('visualizationsTaskRunner', () => {
error: undefined,
state: {
runs: 1,
stats: {
cave_painting: { total: 3, spaces_min: 1, spaces_max: 1, spaces_avg: 1 },
printing_press: { total: 1, spaces_min: 1, spaces_max: 1, spaces_avg: 1 },
cuneiform: { total: 2, spaces_min: 2, spaces_max: 2, spaces_avg: 2 },
floppy_disk: { total: 4, spaces_min: 2, spaces_max: 2, spaces_avg: 2 },
},
stats: expectedStats,
},
});

expect(result.state.stats).toMatchObject(expectedStats);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import { first } from 'rxjs/operators';

import { APICaller, IClusterClient } from 'src/core/server';
import { getNextMidnight } from '../../get_next_midnight';
import { getPastDays } from '../../get_past_days';
import { TaskInstance } from '../../../../../task_manager/server';
import { ESSearchHit } from '../../../../../apm/typings/elasticsearch';

interface VisSummary {
type: string;
space: string;
past_days: number;
}

/*
Expand All @@ -26,7 +28,11 @@ async function getStats(callCluster: APICaller, index: string) {
size: 10000, // elasticsearch index.max_result_window default value
index,
ignoreUnavailable: true,
filterPath: ['hits.hits._id', 'hits.hits._source.visualization'],
filterPath: [
'hits.hits._id',
'hits.hits._source.visualization',
'hits.hits._source.updated_at',
],
body: {
query: {
bool: { filter: { term: { type: 'visualization' } } },
Expand All @@ -43,13 +49,14 @@ async function getStats(callCluster: APICaller, index: string) {
const visSummaries: VisSummary[] = esResponse.hits.hits.map(
(hit: ESSearchHit<{ visState: string }>) => {
const spacePhrases: string[] = hit._id.split(':');
const lastUpdated: string = _.get(hit, '_source.updated_at');
const space = spacePhrases.length === 3 ? spacePhrases[0] : 'default'; // if in a custom space, the format of a saved object ID is space:type:id
const visualization = _.get(hit, '_source.visualization', { visState: '{}' });
const visState: { type?: string } = JSON.parse(visualization.visState);

return {
type: visState.type || '_na_',
space,
past_days: getPastDays(lastUpdated),
};
}
);
Expand All @@ -68,6 +75,9 @@ async function getStats(callCluster: APICaller, index: string) {
spaces_min: _.min(spaceCounts),
spaces_max: _.max(spaceCounts),
spaces_avg: total / spaceCounts.length,
saved_7_days_total: curr.filter((c) => c.past_days <= 7).length,
saved_30_days_total: curr.filter((c) => c.past_days <= 30).length,
saved_90_days_total: curr.filter((c) => c.past_days <= 90).length,
};
});
}
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/oss_telemetry/server/test_utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { APICaller } from 'src/core/server';
import { of } from 'rxjs';
import moment from 'moment';
import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks';
import {
ConcreteTaskInstance,
Expand Down Expand Up @@ -37,6 +38,7 @@ const defaultMockSavedObjects = [
_source: {
type: 'visualization',
visualization: { visState: '{"type": "shell_beads"}' },
updated_at: moment().subtract(7, 'days').startOf('day').toString(),
},
},
];
Expand Down

0 comments on commit 3bbfd11

Please sign in to comment.