Fixed usage data collection for import and copy (#106305)

This commit is contained in:
Joe Portner 2021-07-21 13:16:53 -04:00 committed by GitHub
parent 098bf69560
commit ec30f2aeeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 249 additions and 33 deletions

View file

@ -122,6 +122,7 @@ describe('CoreUsageDataService', () => {
hidden: true,
namespaceType: 'agnostic',
mappings: expect.anything(),
migrations: expect.anything(),
});
});
});

View file

@ -8,6 +8,7 @@
import { SavedObjectsType } from '../saved_objects';
import { CORE_USAGE_STATS_TYPE } from './constants';
import { migrateTo7141 } from './migrations';
/** @internal */
export const coreUsageStatsType: SavedObjectsType = {
@ -18,4 +19,7 @@ export const coreUsageStatsType: SavedObjectsType = {
dynamic: false, // we aren't querying or aggregating over this data, so we don't need to specify any fields
properties: {},
},
migrations: {
'7.14.1': migrateTo7141,
},
};

View file

@ -790,8 +790,14 @@ describe('CoreUsageStatsClient', () => {
createNewCopies: true,
overwrite: true,
} as IncrementSavedObjectsImportOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
await usageStatsClient.incrementSavedObjectsImport({
request,
createNewCopies: false,
overwrite: true,
} as IncrementSavedObjectsImportOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(2);
expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith(
1,
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
@ -799,6 +805,19 @@ describe('CoreUsageStatsClient', () => {
`${IMPORT_STATS_PREFIX}.namespace.default.total`,
`${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`,
// excludes 'overwriteEnabled.yes' and 'overwriteEnabled.no' when createNewCopies is true
],
incrementOptions
);
expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith(
2,
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${IMPORT_STATS_PREFIX}.total`,
`${IMPORT_STATS_PREFIX}.namespace.default.total`,
`${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.yes`,
],
incrementOptions

View file

@ -150,7 +150,7 @@ export class CoreUsageStatsClient {
const { createNewCopies, overwrite } = options;
const counterFieldNames = [
`createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`,
`overwriteEnabled.${overwrite ? 'yes' : 'no'}`,
...(!createNewCopies ? [`overwriteEnabled.${overwrite ? 'yes' : 'no'}`] : []), // the overwrite option is ignored when createNewCopies is true
];
await this.updateUsageStats(counterFieldNames, IMPORT_STATS_PREFIX, options);
}

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SavedObjectUnsanitizedDoc } from '../saved_objects';
import { migrateTo7141 } from './migrations';
import type { CoreUsageStats } from './types';
const type = 'obj-type';
const id = 'obj-id';
describe('#migrateTo7141', () => {
it('Resets targeted counter fields and leaves others unchanged', () => {
const doc = {
type,
id,
attributes: {
foo: 'bar',
'apiCalls.savedObjectsImport.total': 10,
},
} as SavedObjectUnsanitizedDoc<CoreUsageStats>;
expect(migrateTo7141(doc)).toEqual({
type,
id,
attributes: {
foo: 'bar',
'apiCalls.savedObjectsImport.total': 0,
'apiCalls.savedObjectsImport.namespace.default.total': 0,
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes': 0,
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no': 0,
'apiCalls.savedObjectsImport.namespace.custom.total': 0,
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes': 0,
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no': 0,
'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes': 0,
'apiCalls.savedObjectsImport.createNewCopiesEnabled.no': 0,
'apiCalls.savedObjectsImport.overwriteEnabled.yes': 0,
'apiCalls.savedObjectsImport.overwriteEnabled.no': 0,
},
});
});
});

View file

@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { cloneDeep } from 'lodash';
import type { SavedObjectUnsanitizedDoc } from '../saved_objects';
import type { CoreUsageStats } from './types';
export const migrateTo7141 = (doc: SavedObjectUnsanitizedDoc<CoreUsageStats>) => {
try {
return resetFields(doc, [
// Prior to this, we were counting the `overwrite` option incorrectly; reset all import API counter fields so we get clean data
'apiCalls.savedObjectsImport.total',
'apiCalls.savedObjectsImport.namespace.default.total',
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes',
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no',
'apiCalls.savedObjectsImport.namespace.custom.total',
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes',
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no',
'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes',
'apiCalls.savedObjectsImport.createNewCopiesEnabled.no',
'apiCalls.savedObjectsImport.overwriteEnabled.yes',
'apiCalls.savedObjectsImport.overwriteEnabled.no',
]);
} catch (err) {
// fail-safe
}
return doc;
};
function resetFields(
doc: SavedObjectUnsanitizedDoc<CoreUsageStats>,
fieldsToReset: Array<keyof CoreUsageStats>
) {
const newDoc = cloneDeep(doc);
const { attributes = {} } = newDoc;
for (const field of fieldsToReset) {
attributes[field] = 0;
}
return { ...newDoc, attributes };
}

View file

@ -5,4 +5,7 @@
* 2.0.
*/
export { migrateToKibana660 } from './migrate_6x';
import * as spaceMigrations from './space_migrations';
import * as usageStatsMigrations from './usage_stats_migrations';
export { spaceMigrations, usageStatsMigrations };

View file

@ -5,23 +5,18 @@
* 2.0.
*/
import type { SavedObjectMigrationContext } from 'src/core/server';
import type { Space } from 'src/plugins/spaces_oss/common';
import { migrateToKibana660 } from './migrate_6x';
const mockContext = {} as SavedObjectMigrationContext;
import { migrateTo660 } from './space_migrations';
describe('migrateTo660', () => {
it('adds a "disabledFeatures" attribute initialized as an empty array', () => {
expect(
migrateToKibana660(
{
migrateTo660({
id: 'space:foo',
type: 'space',
attributes: {},
},
mockContext
)
attributes: {} as Space,
})
).toEqual({
id: 'space:foo',
type: 'space',
@ -34,16 +29,13 @@ describe('migrateTo660', () => {
it('does not initialize "disabledFeatures" if the property already exists', () => {
// This scenario shouldn't happen organically. Protecting against defects in the migration.
expect(
migrateToKibana660(
{
migrateTo660({
id: 'space:foo',
type: 'space',
attributes: {
disabledFeatures: ['foo', 'bar', 'baz'],
},
},
mockContext
)
} as Space,
})
).toEqual({
id: 'space:foo',
type: 'space',

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import type { SavedObjectMigrationFn } from 'src/core/server';
import type { SavedObjectUnsanitizedDoc } from 'src/core/server';
import type { Space } from 'src/plugins/spaces_oss/common';
export const migrateToKibana660: SavedObjectMigrationFn<any, any> = (doc) => {
export const migrateTo660 = (doc: SavedObjectUnsanitizedDoc<Space>) => {
if (!doc.attributes.hasOwnProperty('disabledFeatures')) {
doc.attributes.disabledFeatures = [];
}

View file

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { SavedObjectUnsanitizedDoc } from 'src/core/server';
import type { UsageStats } from '../../usage_stats';
import { migrateTo7141 } from './usage_stats_migrations';
const type = 'obj-type';
const id = 'obj-id';
describe('#migrateTo7141', () => {
it('Resets targeted counter fields and leaves others unchanged', () => {
const doc = {
type,
id,
attributes: {
foo: 'bar',
'apiCalls.copySavedObjects.total': 10,
},
} as SavedObjectUnsanitizedDoc<UsageStats>;
expect(migrateTo7141(doc)).toEqual({
type,
id,
attributes: {
foo: 'bar',
'apiCalls.copySavedObjects.total': 0,
'apiCalls.copySavedObjects.kibanaRequest.yes': 0,
'apiCalls.copySavedObjects.kibanaRequest.no': 0,
'apiCalls.copySavedObjects.createNewCopiesEnabled.yes': 0,
'apiCalls.copySavedObjects.createNewCopiesEnabled.no': 0,
'apiCalls.copySavedObjects.overwriteEnabled.yes': 0,
'apiCalls.copySavedObjects.overwriteEnabled.no': 0,
},
});
});
});

View file

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { cloneDeep } from 'lodash';
import type { SavedObjectUnsanitizedDoc } from 'src/core/server';
import type { UsageStats } from '../../usage_stats';
export const migrateTo7141 = (doc: SavedObjectUnsanitizedDoc<UsageStats>) => {
try {
return resetFields(doc, [
// Prior to this, we were counting the `overwrite` option incorrectly; reset all copy API counter fields so we get clean data
'apiCalls.copySavedObjects.total',
'apiCalls.copySavedObjects.kibanaRequest.yes',
'apiCalls.copySavedObjects.kibanaRequest.no',
'apiCalls.copySavedObjects.createNewCopiesEnabled.yes',
'apiCalls.copySavedObjects.createNewCopiesEnabled.no',
'apiCalls.copySavedObjects.overwriteEnabled.yes',
'apiCalls.copySavedObjects.overwriteEnabled.no',
]);
} catch (err) {
// fail-safe
}
return doc;
};
function resetFields(
doc: SavedObjectUnsanitizedDoc<UsageStats>,
fieldsToReset: Array<keyof UsageStats>
) {
const newDoc = cloneDeep(doc);
const { attributes = {} } = newDoc;
for (const field of fieldsToReset) {
attributes[field] = 0;
}
return { ...newDoc, attributes };
}

View file

@ -10,7 +10,7 @@ import type { CoreSetup } from 'src/core/server';
import type { SpacesServiceStart } from '../spaces_service';
import { SPACES_USAGE_STATS_TYPE } from '../usage_stats';
import { SpacesSavedObjectMappings, UsageStatsMappings } from './mappings';
import { migrateToKibana660 } from './migrations';
import { spaceMigrations, usageStatsMigrations } from './migrations';
import { spacesSavedObjectsClientWrapperFactory } from './saved_objects_client_wrapper_factory';
interface SetupDeps {
@ -26,7 +26,7 @@ export class SpacesSavedObjectsService {
namespaceType: 'agnostic',
mappings: SpacesSavedObjectMappings,
migrations: {
'6.6.0': migrateToKibana660,
'6.6.0': spaceMigrations.migrateTo660,
},
});
@ -35,6 +35,9 @@ export class SpacesSavedObjectsService {
hidden: true,
namespaceType: 'agnostic',
mappings: UsageStatsMappings,
migrations: {
'7.14.1': usageStatsMigrations.migrateTo7141,
},
});
core.savedObjects.addClientWrapper(

View file

@ -117,14 +117,32 @@ describe('UsageStatsClient', () => {
createNewCopies: true,
overwrite: true,
} as IncrementCopySavedObjectsOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
await usageStatsClient.incrementCopySavedObjects({
headers: firstPartyRequestHeaders,
createNewCopies: false,
overwrite: true,
} as IncrementCopySavedObjectsOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(2);
expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith(
1,
SPACES_USAGE_STATS_TYPE,
SPACES_USAGE_STATS_ID,
[
`${COPY_STATS_PREFIX}.total`,
`${COPY_STATS_PREFIX}.kibanaRequest.yes`,
`${COPY_STATS_PREFIX}.createNewCopiesEnabled.yes`,
// excludes 'overwriteEnabled.yes' and 'overwriteEnabled.no' when createNewCopies is true
],
incrementOptions
);
expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith(
2,
SPACES_USAGE_STATS_TYPE,
SPACES_USAGE_STATS_ID,
[
`${COPY_STATS_PREFIX}.total`,
`${COPY_STATS_PREFIX}.kibanaRequest.yes`,
`${COPY_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${COPY_STATS_PREFIX}.overwriteEnabled.yes`,
],
incrementOptions

View file

@ -71,7 +71,7 @@ export class UsageStatsClient {
'total',
`kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
`createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`,
`overwriteEnabled.${overwrite ? 'yes' : 'no'}`,
...(!createNewCopies ? [`overwriteEnabled.${overwrite ? 'yes' : 'no'}`] : []), // the overwrite option is ignored when createNewCopies is true
];
await this.updateUsageStats(counterFieldNames, COPY_STATS_PREFIX);
}