Update core usage stats collection (#85706) (#85879)

This commit is contained in:
Joe Portner 2020-12-15 14:19:57 -05:00 committed by GitHub
parent b3acc4b693
commit d2d98e8ccf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1591 additions and 172 deletions

View file

@ -29,6 +29,7 @@ import { config as RawHttpConfig } from '../http/http_config';
import { config as RawLoggingConfig } from '../logging/logging_config';
import { config as RawKibanaConfig } from '../kibana_config';
import { savedObjectsConfig as RawSavedObjectsConfig } from '../saved_objects/saved_objects_config';
import { httpServiceMock } from '../http/http_service.mock';
import { metricsServiceMock } from '../metrics/metrics_service.mock';
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
@ -68,11 +69,12 @@ describe('CoreUsageDataService', () => {
describe('setup', () => {
it('creates internal repository', async () => {
const http = httpServiceMock.createInternalSetupContract();
const metrics = metricsServiceMock.createInternalSetupContract();
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
service.setup({ metrics, savedObjectsStartPromise });
service.setup({ http, metrics, savedObjectsStartPromise });
const savedObjects = await savedObjectsStartPromise;
expect(savedObjects.createInternalRepository).toHaveBeenCalledTimes(1);
@ -81,14 +83,12 @@ describe('CoreUsageDataService', () => {
describe('#registerType', () => {
it('registers core usage stats type', async () => {
const http = httpServiceMock.createInternalSetupContract();
const metrics = metricsServiceMock.createInternalSetupContract();
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
const coreUsageData = service.setup({
metrics,
savedObjectsStartPromise,
});
const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise });
const typeRegistry = typeRegistryMock.create();
coreUsageData.registerType(typeRegistry);
@ -104,14 +104,12 @@ describe('CoreUsageDataService', () => {
describe('#getClient', () => {
it('returns client', async () => {
const http = httpServiceMock.createInternalSetupContract();
const metrics = metricsServiceMock.createInternalSetupContract();
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
const coreUsageData = service.setup({
metrics,
savedObjectsStartPromise,
});
const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise });
const usageStatsClient = coreUsageData.getClient();
expect(usageStatsClient).toBeInstanceOf(CoreUsageStatsClient);
@ -122,11 +120,12 @@ describe('CoreUsageDataService', () => {
describe('start', () => {
describe('getCoreUsageData', () => {
it('returns core metrics for default config', async () => {
const http = httpServiceMock.createInternalSetupContract();
const metrics = metricsServiceMock.createInternalSetupContract();
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
service.setup({ metrics, savedObjectsStartPromise });
service.setup({ http, metrics, savedObjectsStartPromise });
const elasticsearch = elasticsearchServiceMock.createStart();
elasticsearch.client.asInternalUser.cat.indices.mockResolvedValueOnce({
body: [
@ -296,6 +295,7 @@ describe('CoreUsageDataService', () => {
observables.push(newObservable);
return newObservable;
});
const http = httpServiceMock.createInternalSetupContract();
const metrics = metricsServiceMock.createInternalSetupContract();
metrics.getOpsMetrics$.mockImplementation(() => {
const newObservable = hot('-a-------');
@ -306,7 +306,7 @@ describe('CoreUsageDataService', () => {
savedObjectsServiceMock.createStartContract()
);
service.setup({ metrics, savedObjectsStartPromise });
service.setup({ http, metrics, savedObjectsStartPromise });
// Use the stopTimer$ to delay calling stop() until the third frame
const stopTimer$ = cold('---a|');

View file

@ -24,7 +24,7 @@ import { CoreService } from 'src/core/types';
import { Logger, SavedObjectsServiceStart, SavedObjectTypeRegistry } from 'src/core/server';
import { CoreContext } from '../core_context';
import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config';
import { HttpConfigType } from '../http';
import { HttpConfigType, InternalHttpServiceSetup } from '../http';
import { LoggingConfigType } from '../logging';
import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config';
import {
@ -42,6 +42,7 @@ import { CoreUsageStatsClient } from './core_usage_stats_client';
import { MetricsServiceSetup, OpsMetrics } from '..';
export interface SetupDeps {
http: InternalHttpServiceSetup;
metrics: MetricsServiceSetup;
savedObjectsStartPromise: Promise<SavedObjectsServiceStart>;
}
@ -248,7 +249,7 @@ export class CoreUsageDataService implements CoreService<CoreUsageDataSetup, Cor
};
}
setup({ metrics, savedObjectsStartPromise }: SetupDeps) {
setup({ http, metrics, savedObjectsStartPromise }: SetupDeps) {
metrics
.getOpsMetrics$()
.pipe(takeUntil(this.stop$))
@ -300,7 +301,7 @@ export class CoreUsageDataService implements CoreService<CoreUsageDataSetup, Cor
const getClient = () => {
const debugLogger = (message: string) => this.logger.debug(message);
return new CoreUsageStatsClient(debugLogger, internalRepositoryPromise);
return new CoreUsageStatsClient(debugLogger, http.basePath, internalRepositoryPromise);
};
this.coreUsageStatsClient = getClient();

View file

@ -22,6 +22,14 @@ import { CoreUsageStatsClient } from '.';
const createUsageStatsClientMock = () =>
(({
getUsageStats: jest.fn().mockResolvedValue({}),
incrementSavedObjectsBulkCreate: jest.fn().mockResolvedValue(null),
incrementSavedObjectsBulkGet: jest.fn().mockResolvedValue(null),
incrementSavedObjectsBulkUpdate: jest.fn().mockResolvedValue(null),
incrementSavedObjectsCreate: jest.fn().mockResolvedValue(null),
incrementSavedObjectsDelete: jest.fn().mockResolvedValue(null),
incrementSavedObjectsFind: jest.fn().mockResolvedValue(null),
incrementSavedObjectsGet: jest.fn().mockResolvedValue(null),
incrementSavedObjectsUpdate: jest.fn().mockResolvedValue(null),
incrementSavedObjectsImport: jest.fn().mockResolvedValue(null),
incrementSavedObjectsResolveImportErrors: jest.fn().mockResolvedValue(null),
incrementSavedObjectsExport: jest.fn().mockResolvedValue(null),

View file

@ -17,30 +17,43 @@
* under the License.
*/
import { savedObjectsRepositoryMock } from '../mocks';
import { httpServerMock, httpServiceMock, savedObjectsRepositoryMock } from '../mocks';
import { CORE_USAGE_STATS_TYPE, CORE_USAGE_STATS_ID } from './constants';
import {
BaseIncrementOptions,
IncrementSavedObjectsImportOptions,
IncrementSavedObjectsResolveImportErrorsOptions,
IncrementSavedObjectsExportOptions,
BULK_CREATE_STATS_PREFIX,
BULK_GET_STATS_PREFIX,
BULK_UPDATE_STATS_PREFIX,
CREATE_STATS_PREFIX,
DELETE_STATS_PREFIX,
FIND_STATS_PREFIX,
GET_STATS_PREFIX,
UPDATE_STATS_PREFIX,
IMPORT_STATS_PREFIX,
RESOLVE_IMPORT_STATS_PREFIX,
EXPORT_STATS_PREFIX,
} from './core_usage_stats_client';
import { CoreUsageStatsClient } from '.';
import { DEFAULT_NAMESPACE_STRING } from '../saved_objects/service/lib/utils';
describe('CoreUsageStatsClient', () => {
const setup = () => {
const setup = (namespace?: string) => {
const debugLoggerMock = jest.fn();
const basePathMock = httpServiceMock.createBasePath();
// we could mock a return value for basePathMock.get, but it isn't necessary for testing purposes
basePathMock.remove.mockReturnValue(namespace ? `/s/${namespace}` : '/');
const repositoryMock = savedObjectsRepositoryMock.create();
const usageStatsClient = new CoreUsageStatsClient(
debugLoggerMock,
basePathMock,
Promise.resolve(repositoryMock)
);
return { usageStatsClient, debugLoggerMock, repositoryMock };
return { usageStatsClient, debugLoggerMock, basePathMock, repositoryMock };
};
const firstPartyRequestHeaders = { 'kbn-version': 'a', origin: 'b', referer: 'c' }; // as long as these three header fields are truthy, this will be treated like a first-party request
const firstPartyRequestHeaders = { 'kbn-version': 'a', referer: 'b' }; // as long as these two header fields are truthy, this will be treated like a first-party request
const incrementOptions = { refresh: false };
describe('#getUsageStats', () => {
@ -67,13 +80,616 @@ describe('CoreUsageStatsClient', () => {
});
});
describe('#incrementSavedObjectsBulkCreate', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsBulkCreate({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_CREATE_STATS_PREFIX}.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.default.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsBulkCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_CREATE_STATS_PREFIX}.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.default.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_CREATE_STATS_PREFIX}.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.custom.total`,
`${BULK_CREATE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsBulkGet', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsBulkGet({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_GET_STATS_PREFIX}.total`,
`${BULK_GET_STATS_PREFIX}.namespace.default.total`,
`${BULK_GET_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsBulkGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_GET_STATS_PREFIX}.total`,
`${BULK_GET_STATS_PREFIX}.namespace.default.total`,
`${BULK_GET_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_GET_STATS_PREFIX}.total`,
`${BULK_GET_STATS_PREFIX}.namespace.custom.total`,
`${BULK_GET_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsBulkUpdate', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsBulkUpdate({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_UPDATE_STATS_PREFIX}.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.default.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsBulkUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_UPDATE_STATS_PREFIX}.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.default.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsBulkUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${BULK_UPDATE_STATS_PREFIX}.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.custom.total`,
`${BULK_UPDATE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsCreate', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsCreate({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${CREATE_STATS_PREFIX}.total`,
`${CREATE_STATS_PREFIX}.namespace.default.total`,
`${CREATE_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${CREATE_STATS_PREFIX}.total`,
`${CREATE_STATS_PREFIX}.namespace.default.total`,
`${CREATE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsCreate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${CREATE_STATS_PREFIX}.total`,
`${CREATE_STATS_PREFIX}.namespace.custom.total`,
`${CREATE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsDelete', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsDelete({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsDelete({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${DELETE_STATS_PREFIX}.total`,
`${DELETE_STATS_PREFIX}.namespace.default.total`,
`${DELETE_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsDelete({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${DELETE_STATS_PREFIX}.total`,
`${DELETE_STATS_PREFIX}.namespace.default.total`,
`${DELETE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsDelete({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${DELETE_STATS_PREFIX}.total`,
`${DELETE_STATS_PREFIX}.namespace.custom.total`,
`${DELETE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsFind', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsFind({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsFind({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${FIND_STATS_PREFIX}.total`,
`${FIND_STATS_PREFIX}.namespace.default.total`,
`${FIND_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsFind({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${FIND_STATS_PREFIX}.total`,
`${FIND_STATS_PREFIX}.namespace.default.total`,
`${FIND_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsFind({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${FIND_STATS_PREFIX}.total`,
`${FIND_STATS_PREFIX}.namespace.custom.total`,
`${FIND_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsGet', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsGet({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${GET_STATS_PREFIX}.total`,
`${GET_STATS_PREFIX}.namespace.default.total`,
`${GET_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${GET_STATS_PREFIX}.total`,
`${GET_STATS_PREFIX}.namespace.default.total`,
`${GET_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsGet({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${GET_STATS_PREFIX}.total`,
`${GET_STATS_PREFIX}.namespace.custom.total`,
`${GET_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsUpdate', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsUpdate({
request,
} as BaseIncrementOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${UPDATE_STATS_PREFIX}.total`,
`${UPDATE_STATS_PREFIX}.namespace.default.total`,
`${UPDATE_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
],
incrementOptions
);
});
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${UPDATE_STATS_PREFIX}.total`,
`${UPDATE_STATS_PREFIX}.namespace.default.total`,
`${UPDATE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsUpdate({
request,
} as BaseIncrementOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${UPDATE_STATS_PREFIX}.total`,
`${UPDATE_STATS_PREFIX}.namespace.custom.total`,
`${UPDATE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsImport', () => {
it('does not throw an error if repository incrementCounter operation fails', async () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsImport({} as IncrementSavedObjectsImportOptions)
usageStatsClient.incrementSavedObjectsImport({
request,
} as IncrementSavedObjectsImportOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
});
@ -81,14 +697,18 @@ describe('CoreUsageStatsClient', () => {
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
await usageStatsClient.incrementSavedObjectsImport({} as IncrementSavedObjectsImportOptions);
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsImport({
request,
} as IncrementSavedObjectsImportOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${IMPORT_STATS_PREFIX}.total`,
`${IMPORT_STATS_PREFIX}.kibanaRequest.no`,
`${IMPORT_STATS_PREFIX}.namespace.default.total`,
`${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.no`,
],
@ -96,11 +716,12 @@ describe('CoreUsageStatsClient', () => {
);
});
it('handles truthy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsImport({
headers: firstPartyRequestHeaders,
request,
createNewCopies: true,
overwrite: true,
} as IncrementSavedObjectsImportOptions);
@ -110,13 +731,36 @@ describe('CoreUsageStatsClient', () => {
CORE_USAGE_STATS_ID,
[
`${IMPORT_STATS_PREFIX}.total`,
`${IMPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${IMPORT_STATS_PREFIX}.namespace.default.total`,
`${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsImport({
request,
} as IncrementSavedObjectsImportOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${IMPORT_STATS_PREFIX}.total`,
`${IMPORT_STATS_PREFIX}.namespace.custom.total`,
`${IMPORT_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsResolveImportErrors', () => {
@ -124,10 +768,11 @@ describe('CoreUsageStatsClient', () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsResolveImportErrors(
{} as IncrementSavedObjectsResolveImportErrorsOptions
)
usageStatsClient.incrementSavedObjectsResolveImportErrors({
request,
} as IncrementSavedObjectsResolveImportErrorsOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
@ -135,27 +780,30 @@ describe('CoreUsageStatsClient', () => {
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
await usageStatsClient.incrementSavedObjectsResolveImportErrors(
{} as IncrementSavedObjectsResolveImportErrorsOptions
);
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsResolveImportErrors({
request,
} as IncrementSavedObjectsResolveImportErrorsOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${RESOLVE_IMPORT_STATS_PREFIX}.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.kibanaRequest.no`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.default.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
`${RESOLVE_IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
],
incrementOptions
);
});
it('handles truthy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsResolveImportErrors({
headers: firstPartyRequestHeaders,
request,
createNewCopies: true,
} as IncrementSavedObjectsResolveImportErrorsOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
@ -164,12 +812,34 @@ describe('CoreUsageStatsClient', () => {
CORE_USAGE_STATS_ID,
[
`${RESOLVE_IMPORT_STATS_PREFIX}.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.default.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
`${RESOLVE_IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsResolveImportErrors({
request,
} as IncrementSavedObjectsResolveImportErrorsOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${RESOLVE_IMPORT_STATS_PREFIX}.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.custom.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
`${RESOLVE_IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
],
incrementOptions
);
});
});
describe('#incrementSavedObjectsExport', () => {
@ -177,8 +847,11 @@ describe('CoreUsageStatsClient', () => {
const { usageStatsClient, repositoryMock } = setup();
repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!'));
const request = httpServerMock.createKibanaRequest();
await expect(
usageStatsClient.incrementSavedObjectsExport({} as IncrementSavedObjectsExportOptions)
usageStatsClient.incrementSavedObjectsExport({
request,
} as IncrementSavedObjectsExportOptions)
).resolves.toBeUndefined();
expect(repositoryMock.incrementCounter).toHaveBeenCalled();
});
@ -186,7 +859,9 @@ describe('CoreUsageStatsClient', () => {
it('handles falsy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsExport({
request,
types: undefined,
supportedTypes: ['foo', 'bar'],
} as IncrementSavedObjectsExportOptions);
@ -196,18 +871,20 @@ describe('CoreUsageStatsClient', () => {
CORE_USAGE_STATS_ID,
[
`${EXPORT_STATS_PREFIX}.total`,
`${EXPORT_STATS_PREFIX}.kibanaRequest.no`,
`${EXPORT_STATS_PREFIX}.namespace.default.total`,
`${EXPORT_STATS_PREFIX}.namespace.default.kibanaRequest.no`,
`${EXPORT_STATS_PREFIX}.allTypesSelected.no`,
],
incrementOptions
);
});
it('handles truthy options appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup();
it('handles truthy options and the default namespace string appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING);
const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders });
await usageStatsClient.incrementSavedObjectsExport({
headers: firstPartyRequestHeaders,
request,
types: ['foo', 'bar'],
supportedTypes: ['foo', 'bar'],
} as IncrementSavedObjectsExportOptions);
@ -217,11 +894,33 @@ describe('CoreUsageStatsClient', () => {
CORE_USAGE_STATS_ID,
[
`${EXPORT_STATS_PREFIX}.total`,
`${EXPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${EXPORT_STATS_PREFIX}.namespace.default.total`,
`${EXPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`,
`${EXPORT_STATS_PREFIX}.allTypesSelected.yes`,
],
incrementOptions
);
});
it('handles a non-default space appropriately', async () => {
const { usageStatsClient, repositoryMock } = setup('foo');
const request = httpServerMock.createKibanaRequest();
await usageStatsClient.incrementSavedObjectsExport({
request,
} as IncrementSavedObjectsExportOptions);
expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1);
expect(repositoryMock.incrementCounter).toHaveBeenCalledWith(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
[
`${EXPORT_STATS_PREFIX}.total`,
`${EXPORT_STATS_PREFIX}.namespace.custom.total`,
`${EXPORT_STATS_PREFIX}.namespace.custom.kibanaRequest.no`,
`${EXPORT_STATS_PREFIX}.allTypesSelected.no`,
],
incrementOptions
);
});
});
});

View file

@ -19,16 +19,19 @@
import { CORE_USAGE_STATS_TYPE, CORE_USAGE_STATS_ID } from './constants';
import { CoreUsageStats } from './types';
import { DEFAULT_NAMESPACE_STRING } from '../saved_objects/service/lib/utils';
import {
Headers,
ISavedObjectsRepository,
SavedObjectsImportOptions,
SavedObjectsResolveImportErrorsOptions,
SavedObjectsExportOptions,
KibanaRequest,
IBasePath,
} from '..';
interface BaseIncrementOptions {
headers?: Headers;
/** @internal */
export interface BaseIncrementOptions {
request: KibanaRequest;
}
/** @internal */
export type IncrementSavedObjectsImportOptions = BaseIncrementOptions &
@ -40,33 +43,47 @@ export type IncrementSavedObjectsResolveImportErrorsOptions = BaseIncrementOptio
export type IncrementSavedObjectsExportOptions = BaseIncrementOptions &
Pick<SavedObjectsExportOptions, 'types'> & { supportedTypes: string[] };
export const BULK_CREATE_STATS_PREFIX = 'apiCalls.savedObjectsBulkCreate';
export const BULK_GET_STATS_PREFIX = 'apiCalls.savedObjectsBulkGet';
export const BULK_UPDATE_STATS_PREFIX = 'apiCalls.savedObjectsBulkUpdate';
export const CREATE_STATS_PREFIX = 'apiCalls.savedObjectsCreate';
export const DELETE_STATS_PREFIX = 'apiCalls.savedObjectsDelete';
export const FIND_STATS_PREFIX = 'apiCalls.savedObjectsFind';
export const GET_STATS_PREFIX = 'apiCalls.savedObjectsGet';
export const UPDATE_STATS_PREFIX = 'apiCalls.savedObjectsUpdate';
export const IMPORT_STATS_PREFIX = 'apiCalls.savedObjectsImport';
export const RESOLVE_IMPORT_STATS_PREFIX = 'apiCalls.savedObjectsResolveImportErrors';
export const EXPORT_STATS_PREFIX = 'apiCalls.savedObjectsExport';
const ALL_COUNTER_FIELDS = [
`${IMPORT_STATS_PREFIX}.total`,
`${IMPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${IMPORT_STATS_PREFIX}.kibanaRequest.no`,
// Saved Objects Client APIs
...getFieldsForCounter(BULK_CREATE_STATS_PREFIX),
...getFieldsForCounter(BULK_GET_STATS_PREFIX),
...getFieldsForCounter(BULK_UPDATE_STATS_PREFIX),
...getFieldsForCounter(CREATE_STATS_PREFIX),
...getFieldsForCounter(DELETE_STATS_PREFIX),
...getFieldsForCounter(FIND_STATS_PREFIX),
...getFieldsForCounter(GET_STATS_PREFIX),
...getFieldsForCounter(UPDATE_STATS_PREFIX),
// Saved Objects Management APIs
...getFieldsForCounter(IMPORT_STATS_PREFIX),
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`,
`${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.yes`,
`${IMPORT_STATS_PREFIX}.overwriteEnabled.no`,
`${RESOLVE_IMPORT_STATS_PREFIX}.total`,
`${RESOLVE_IMPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${RESOLVE_IMPORT_STATS_PREFIX}.kibanaRequest.no`,
...getFieldsForCounter(RESOLVE_IMPORT_STATS_PREFIX),
`${RESOLVE_IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`,
`${RESOLVE_IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`,
`${EXPORT_STATS_PREFIX}.total`,
`${EXPORT_STATS_PREFIX}.kibanaRequest.yes`,
`${EXPORT_STATS_PREFIX}.kibanaRequest.no`,
...getFieldsForCounter(EXPORT_STATS_PREFIX),
`${EXPORT_STATS_PREFIX}.allTypesSelected.yes`,
`${EXPORT_STATS_PREFIX}.allTypesSelected.no`,
];
const SPACE_CONTEXT_REGEX = /^\/s\/([a-z0-9_\-]+)/;
/** @internal */
export class CoreUsageStatsClient {
constructor(
private readonly debugLogger: (message: string) => void,
private readonly basePath: IBasePath,
private readonly repositoryPromise: Promise<ISavedObjectsRepository>
) {}
@ -88,67 +105,128 @@ export class CoreUsageStatsClient {
return coreUsageStats;
}
public async incrementSavedObjectsImport({
headers,
createNewCopies,
overwrite,
}: IncrementSavedObjectsImportOptions) {
const isKibanaRequest = getIsKibanaRequest(headers);
public async incrementSavedObjectsBulkCreate(options: BaseIncrementOptions) {
await this.updateUsageStats([], BULK_CREATE_STATS_PREFIX, options);
}
public async incrementSavedObjectsBulkGet(options: BaseIncrementOptions) {
await this.updateUsageStats([], BULK_GET_STATS_PREFIX, options);
}
public async incrementSavedObjectsBulkUpdate(options: BaseIncrementOptions) {
await this.updateUsageStats([], BULK_UPDATE_STATS_PREFIX, options);
}
public async incrementSavedObjectsCreate(options: BaseIncrementOptions) {
await this.updateUsageStats([], CREATE_STATS_PREFIX, options);
}
public async incrementSavedObjectsDelete(options: BaseIncrementOptions) {
await this.updateUsageStats([], DELETE_STATS_PREFIX, options);
}
public async incrementSavedObjectsFind(options: BaseIncrementOptions) {
await this.updateUsageStats([], FIND_STATS_PREFIX, options);
}
public async incrementSavedObjectsGet(options: BaseIncrementOptions) {
await this.updateUsageStats([], GET_STATS_PREFIX, options);
}
public async incrementSavedObjectsUpdate(options: BaseIncrementOptions) {
await this.updateUsageStats([], UPDATE_STATS_PREFIX, options);
}
public async incrementSavedObjectsImport(options: IncrementSavedObjectsImportOptions) {
const { createNewCopies, overwrite } = options;
const counterFieldNames = [
'total',
`kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
`createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`,
`overwriteEnabled.${overwrite ? 'yes' : 'no'}`,
];
await this.updateUsageStats(counterFieldNames, IMPORT_STATS_PREFIX);
await this.updateUsageStats(counterFieldNames, IMPORT_STATS_PREFIX, options);
}
public async incrementSavedObjectsResolveImportErrors({
headers,
createNewCopies,
}: IncrementSavedObjectsResolveImportErrorsOptions) {
const isKibanaRequest = getIsKibanaRequest(headers);
const counterFieldNames = [
'total',
`kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
`createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`,
];
await this.updateUsageStats(counterFieldNames, RESOLVE_IMPORT_STATS_PREFIX);
public async incrementSavedObjectsResolveImportErrors(
options: IncrementSavedObjectsResolveImportErrorsOptions
) {
const { createNewCopies } = options;
const counterFieldNames = [`createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`];
await this.updateUsageStats(counterFieldNames, RESOLVE_IMPORT_STATS_PREFIX, options);
}
public async incrementSavedObjectsExport({
headers,
types,
supportedTypes,
}: IncrementSavedObjectsExportOptions) {
const isKibanaRequest = getIsKibanaRequest(headers);
public async incrementSavedObjectsExport(options: IncrementSavedObjectsExportOptions) {
const { types, supportedTypes } = options;
const isAllTypesSelected = !!types && supportedTypes.every((x) => types.includes(x));
const counterFieldNames = [
'total',
`kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
`allTypesSelected.${isAllTypesSelected ? 'yes' : 'no'}`,
];
await this.updateUsageStats(counterFieldNames, EXPORT_STATS_PREFIX);
const counterFieldNames = [`allTypesSelected.${isAllTypesSelected ? 'yes' : 'no'}`];
await this.updateUsageStats(counterFieldNames, EXPORT_STATS_PREFIX, options);
}
private async updateUsageStats(counterFieldNames: string[], prefix: string) {
private async updateUsageStats(
counterFieldNames: string[],
prefix: string,
{ request }: BaseIncrementOptions
) {
const options = { refresh: false };
try {
const repository = await this.repositoryPromise;
const fields = this.getFieldsToIncrement(counterFieldNames, prefix, request);
await repository.incrementCounter(
CORE_USAGE_STATS_TYPE,
CORE_USAGE_STATS_ID,
counterFieldNames.map((x) => `${prefix}.${x}`),
fields,
options
);
} catch (err) {
// do nothing
}
}
private getIsDefaultNamespace(request: KibanaRequest) {
const requestBasePath = this.basePath.get(request); // obtain the original request basePath, as it may have been modified by a request interceptor
const pathToCheck = this.basePath.remove(requestBasePath); // remove the server basePath from the request basePath
const matchResult = pathToCheck.match(SPACE_CONTEXT_REGEX); // Look for `/s/space-url-context` in the base path
if (!matchResult || matchResult.length === 0) {
return true;
}
// Ignoring first result, we only want the capture group result at index 1
const [, spaceId] = matchResult;
return spaceId === DEFAULT_NAMESPACE_STRING;
}
private getFieldsToIncrement(
counterFieldNames: string[],
prefix: string,
request: KibanaRequest
) {
const isKibanaRequest = getIsKibanaRequest(request);
const isDefaultNamespace = this.getIsDefaultNamespace(request);
const namespaceField = isDefaultNamespace ? 'default' : 'custom';
return [
'total',
`namespace.${namespaceField}.total`,
`namespace.${namespaceField}.kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
...counterFieldNames,
].map((x) => `${prefix}.${x}`);
}
}
function getIsKibanaRequest(headers?: Headers) {
// The presence of these three request headers gives us a good indication that this is a first-party request from the Kibana client.
// We can't be 100% certain, but this is a reasonable attempt.
return headers && headers['kbn-version'] && headers.origin && headers.referer;
function getFieldsForCounter(prefix: string) {
return [
'total',
'namespace.default.total',
'namespace.default.kibanaRequest.yes',
'namespace.default.kibanaRequest.no',
'namespace.custom.total',
'namespace.custom.kibanaRequest.yes',
'namespace.custom.kibanaRequest.no',
].map((x) => `${prefix}.${x}`);
}
function getIsKibanaRequest({ headers }: KibanaRequest) {
// The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client.
// We can't be 100% certain, but this is a reasonable attempt.
return headers && headers['kbn-version'] && headers.referer;
}

View file

@ -27,21 +27,91 @@ import { ISavedObjectTypeRegistry, SavedObjectTypeRegistry } from '..';
* includes point-in-time configuration information.
* */
export interface CoreUsageStats {
// Saved Objects Client APIs
'apiCalls.savedObjectsBulkCreate.total'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.default.total'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.custom.total'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsBulkGet.total'?: number;
'apiCalls.savedObjectsBulkGet.namespace.default.total'?: number;
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsBulkGet.namespace.custom.total'?: number;
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsBulkUpdate.total'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.default.total'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.custom.total'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsCreate.total'?: number;
'apiCalls.savedObjectsCreate.namespace.default.total'?: number;
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsCreate.namespace.custom.total'?: number;
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsDelete.total'?: number;
'apiCalls.savedObjectsDelete.namespace.default.total'?: number;
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsDelete.namespace.custom.total'?: number;
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsFind.total'?: number;
'apiCalls.savedObjectsFind.namespace.default.total'?: number;
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsFind.namespace.custom.total'?: number;
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsGet.total'?: number;
'apiCalls.savedObjectsGet.namespace.default.total'?: number;
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsGet.namespace.custom.total'?: number;
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsUpdate.total'?: number;
'apiCalls.savedObjectsUpdate.namespace.default.total'?: number;
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsUpdate.namespace.custom.total'?: number;
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.no'?: number;
// Saved Objects Management APIs
'apiCalls.savedObjectsImport.total'?: number;
'apiCalls.savedObjectsImport.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsImport.kibanaRequest.no'?: number;
'apiCalls.savedObjectsImport.namespace.default.total'?: number;
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsImport.namespace.custom.total'?: number;
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes'?: number;
'apiCalls.savedObjectsImport.createNewCopiesEnabled.no'?: number;
'apiCalls.savedObjectsImport.overwriteEnabled.yes'?: number;
'apiCalls.savedObjectsImport.overwriteEnabled.no'?: number;
'apiCalls.savedObjectsResolveImportErrors.total'?: number;
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.no'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.default.total'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.total'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.yes'?: number;
'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.no'?: number;
'apiCalls.savedObjectsExport.total'?: number;
'apiCalls.savedObjectsExport.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsExport.kibanaRequest.no'?: number;
'apiCalls.savedObjectsExport.namespace.default.total'?: number;
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.no'?: number;
'apiCalls.savedObjectsExport.namespace.custom.total'?: number;
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.no'?: number;
'apiCalls.savedObjectsExport.allTypesSelected.yes'?: number;
'apiCalls.savedObjectsExport.allTypesSelected.no'?: number;
}

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerBulkCreateRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerBulkCreateRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.post(
{
path: '/_bulk_create',
@ -51,6 +56,10 @@ export const registerBulkCreateRoute = (router: IRouter) => {
},
router.handleLegacyErrors(async (context, req, res) => {
const { overwrite } = req.query;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkCreate({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.bulkCreate(req.body, { overwrite });
return res.ok({ body: result });
})

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerBulkGetRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerBulkGetRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.post(
{
path: '/_bulk_get',
@ -35,6 +40,9 @@ export const registerBulkGetRoute = (router: IRouter) => {
},
},
router.handleLegacyErrors(async (context, req, res) => {
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.bulkGet(req.body);
return res.ok({ body: result });
})

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerBulkUpdateRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerBulkUpdateRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.put(
{
path: '/_bulk_update',
@ -46,6 +51,9 @@ export const registerBulkUpdateRoute = (router: IRouter) => {
},
},
router.handleLegacyErrors(async (context, req, res) => {
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {});
const savedObject = await context.core.savedObjects.client.bulkUpdate(req.body);
return res.ok({ body: savedObject });
})

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerCreateRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.post(
{
path: '/{type}/{id?}',
@ -53,6 +58,9 @@ export const registerCreateRoute = (router: IRouter) => {
const { overwrite } = req.query;
const { attributes, migrationVersion, references, initialNamespaces } = req.body;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsCreate({ request: req }).catch(() => {});
const options = { id, overwrite, migrationVersion, references, initialNamespaces };
const result = await context.core.savedObjects.client.create(type, attributes, options);
return res.ok({ body: result });

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerDeleteRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerDeleteRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.delete(
{
path: '/{type}/{id}',
@ -37,6 +42,10 @@ export const registerDeleteRoute = (router: IRouter) => {
router.handleLegacyErrors(async (context, req, res) => {
const { type, id } = req.params;
const { force } = req.query;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsDelete({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.delete(type, id, { force });
return res.ok({ body: result });
})

View file

@ -104,10 +104,9 @@ export const registerExportRoute = (
}
}
const { headers } = req;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient
.incrementSavedObjectsExport({ headers, types, supportedTypes })
.incrementSavedObjectsExport({ request: req, types, supportedTypes })
.catch(() => {});
const exportStream = await exportSavedObjectsToStream({

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerFindRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerFindRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
const referenceSchema = schema.object({
type: schema.string(),
id: schema.string(),
@ -61,6 +66,9 @@ export const registerFindRoute = (router: IRouter) => {
const namespaces =
typeof req.query.namespaces === 'string' ? [req.query.namespaces] : req.query.namespaces;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsFind({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.find({
perPage: query.per_page,
page: query.page,

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerGetRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerGetRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.get(
{
path: '/{type}/{id}',
@ -33,6 +38,10 @@ export const registerGetRoute = (router: IRouter) => {
},
router.handleLegacyErrors(async (context, req, res) => {
const { type, id } = req.params;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsGet({ request: req }).catch(() => {});
const savedObject = await context.core.savedObjects.client.get(type, id);
return res.ok({ body: savedObject });
})

View file

@ -75,10 +75,9 @@ export const registerImportRoute = (
router.handleLegacyErrors(async (context, req, res) => {
const { overwrite, createNewCopies } = req.query;
const { headers } = req;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient
.incrementSavedObjectsImport({ headers, createNewCopies, overwrite })
.incrementSavedObjectsImport({ request: req, createNewCopies, overwrite })
.catch(() => {});
const file = req.body.file as FileStream;

View file

@ -51,14 +51,14 @@ export function registerRoutes({
}) {
const router = http.createRouter('/api/saved_objects/');
registerGetRoute(router);
registerCreateRoute(router);
registerDeleteRoute(router);
registerFindRoute(router);
registerUpdateRoute(router);
registerBulkGetRoute(router);
registerBulkCreateRoute(router);
registerBulkUpdateRoute(router);
registerGetRoute(router, { coreUsageData });
registerCreateRoute(router, { coreUsageData });
registerDeleteRoute(router, { coreUsageData });
registerFindRoute(router, { coreUsageData });
registerUpdateRoute(router, { coreUsageData });
registerBulkGetRoute(router, { coreUsageData });
registerBulkCreateRoute(router, { coreUsageData });
registerBulkUpdateRoute(router, { coreUsageData });
registerLogLegacyImportRoute(router, logger);
registerExportRoute(router, { config, coreUsageData });
registerImportRoute(router, { config, coreUsageData });

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerBulkCreateRoute } from '../bulk_create';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,6 +33,7 @@ describe('POST /api/saved_objects/_bulk_create', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
({ server, httpSetup, handlerContext } = await setupServer());
@ -37,7 +41,10 @@ describe('POST /api/saved_objects/_bulk_create', () => {
savedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [] });
const router = httpSetup.createRouter('/api/saved_objects/');
registerBulkCreateRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsBulkCreate.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerBulkCreateRoute(router, { coreUsageData });
await server.start();
});
@ -46,7 +53,7 @@ describe('POST /api/saved_objects/_bulk_create', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const clientResponse = {
saved_objects: [
{
@ -75,6 +82,9 @@ describe('POST /api/saved_objects/_bulk_create', () => {
.expect(200);
expect(result.body).toEqual(clientResponse);
expect(coreUsageStatsClient.incrementSavedObjectsBulkCreate).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.bulkCreate', async () => {

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerBulkGetRoute } from '../bulk_get';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,6 +33,7 @@ describe('POST /api/saved_objects/_bulk_get', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
({ server, httpSetup, handlerContext } = await setupServer());
@ -39,7 +43,10 @@ describe('POST /api/saved_objects/_bulk_get', () => {
saved_objects: [],
});
const router = httpSetup.createRouter('/api/saved_objects/');
registerBulkGetRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsBulkGet.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerBulkGetRoute(router, { coreUsageData });
await server.start();
});
@ -48,7 +55,7 @@ describe('POST /api/saved_objects/_bulk_get', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const clientResponse = {
saved_objects: [
{
@ -74,6 +81,9 @@ describe('POST /api/saved_objects/_bulk_get', () => {
.expect(200);
expect(result.body).toEqual(clientResponse);
expect(coreUsageStatsClient.incrementSavedObjectsBulkGet).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.bulkGet', async () => {

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerBulkUpdateRoute } from '../bulk_update';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,13 +33,17 @@ describe('PUT /api/saved_objects/_bulk_update', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
({ server, httpSetup, handlerContext } = await setupServer());
savedObjectsClient = handlerContext.savedObjects.client;
const router = httpSetup.createRouter('/api/saved_objects/');
registerBulkUpdateRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsBulkUpdate.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerBulkUpdateRoute(router, { coreUsageData });
await server.start();
});
@ -45,7 +52,7 @@ describe('PUT /api/saved_objects/_bulk_update', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const time = Date.now().toLocaleString();
const clientResponse = [
{
@ -92,6 +99,9 @@ describe('PUT /api/saved_objects/_bulk_update', () => {
.expect(200);
expect(result.body).toEqual({ saved_objects: clientResponse });
expect(coreUsageStatsClient.incrementSavedObjectsBulkUpdate).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.bulkUpdate', async () => {

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerCreateRoute } from '../create';
import { savedObjectsClientMock } from '../../service/saved_objects_client.mock';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,6 +33,7 @@ describe('POST /api/saved_objects/{type}', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
const clientResponse = {
id: 'logstash-*',
@ -46,7 +50,10 @@ describe('POST /api/saved_objects/{type}', () => {
savedObjectsClient.create.mockImplementation(() => Promise.resolve(clientResponse));
const router = httpSetup.createRouter('/api/saved_objects/');
registerCreateRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsCreate.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerCreateRoute(router, { coreUsageData });
await server.start();
});
@ -55,7 +62,7 @@ describe('POST /api/saved_objects/{type}', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const result = await supertest(httpSetup.server.listener)
.post('/api/saved_objects/index-pattern')
.send({
@ -66,6 +73,9 @@ describe('POST /api/saved_objects/{type}', () => {
.expect(200);
expect(result.body).toEqual(clientResponse);
expect(coreUsageStatsClient.incrementSavedObjectsCreate).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('requires attributes', async () => {

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerDeleteRoute } from '../delete';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,13 +33,17 @@ describe('DELETE /api/saved_objects/{type}/{id}', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
({ server, httpSetup, handlerContext } = await setupServer());
savedObjectsClient = handlerContext.savedObjects.client;
const router = httpSetup.createRouter('/api/saved_objects/');
registerDeleteRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsDelete.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerDeleteRoute(router, { coreUsageData });
await server.start();
});
@ -45,12 +52,15 @@ describe('DELETE /api/saved_objects/{type}/{id}', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const result = await supertest(httpSetup.server.listener)
.delete('/api/saved_objects/index-pattern/logstash-*')
.expect(200);
expect(result.body).toEqual({});
expect(coreUsageStatsClient.incrementSavedObjectsDelete).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.delete', async () => {

View file

@ -54,7 +54,7 @@ describe('POST /api/saved_objects/_export', () => {
const router = httpSetup.createRouter('/api/saved_objects/');
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsExport.mockRejectedValue(new Error('Oh no!')); // this error is intentionally swallowed so the export does not fail
coreUsageStatsClient.incrementSavedObjectsExport.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerExportRoute(router, { config, coreUsageData });
@ -118,7 +118,7 @@ describe('POST /api/saved_objects/_export', () => {
})
);
expect(coreUsageStatsClient.incrementSavedObjectsExport).toHaveBeenCalledWith({
headers: expect.anything(),
request: expect.anything(),
types: ['search'],
supportedTypes: ['index-pattern', 'search'],
});

View file

@ -23,6 +23,9 @@ import querystring from 'querystring';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerFindRoute } from '../find';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -32,6 +35,7 @@ describe('GET /api/saved_objects/_find', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
const clientResponse = {
total: 0,
@ -47,7 +51,10 @@ describe('GET /api/saved_objects/_find', () => {
savedObjectsClient.find.mockResolvedValue(clientResponse);
const router = httpSetup.createRouter('/api/saved_objects/');
registerFindRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsFind.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerFindRoute(router, { coreUsageData });
await server.start();
});
@ -66,7 +73,7 @@ describe('GET /api/saved_objects/_find', () => {
);
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const findResponse = {
total: 2,
per_page: 2,
@ -103,6 +110,9 @@ describe('GET /api/saved_objects/_find', () => {
.expect(200);
expect(result.body).toEqual(findResponse);
expect(coreUsageStatsClient.incrementSavedObjectsFind).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.find with defaults', async () => {

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { registerGetRoute } from '../get';
import { ContextService } from '../../../context';
import { savedObjectsClientMock } from '../../service/saved_objects_client.mock';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { HttpService, InternalHttpServiceSetup } from '../../../http';
import { createHttpServer, createCoreContext } from '../../../http/test_utils';
import { coreMock } from '../../../mocks';
@ -32,6 +35,7 @@ describe('GET /api/saved_objects/{type}/{id}', () => {
let httpSetup: InternalHttpServiceSetup;
let handlerContext: ReturnType<typeof coreMock.createRequestHandlerContext>;
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
@ -50,7 +54,10 @@ describe('GET /api/saved_objects/{type}/{id}', () => {
});
const router = httpSetup.createRouter('/api/saved_objects/');
registerGetRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsGet.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerGetRoute(router, { coreUsageData });
await server.start();
});
@ -59,7 +66,7 @@ describe('GET /api/saved_objects/{type}/{id}', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const clientResponse = {
id: 'logstash-*',
title: 'logstash-*',
@ -77,6 +84,9 @@ describe('GET /api/saved_objects/{type}/{id}', () => {
.expect(200);
expect(result.body).toEqual(clientResponse);
expect(coreUsageStatsClient.incrementSavedObjectsGet).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.get', async () => {

View file

@ -76,7 +76,7 @@ describe(`POST ${URL}`, () => {
const router = httpSetup.createRouter('/internal/saved_objects/');
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsImport.mockRejectedValue(new Error('Oh no!')); // this error is intentionally swallowed so the import does not fail
coreUsageStatsClient.incrementSavedObjectsImport.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerImportRoute(router, { config, coreUsageData });
@ -106,7 +106,7 @@ describe(`POST ${URL}`, () => {
expect(result.body).toEqual({ success: true, successCount: 0 });
expect(savedObjectsClient.bulkCreate).not.toHaveBeenCalled(); // no objects were created
expect(coreUsageStatsClient.incrementSavedObjectsImport).toHaveBeenCalledWith({
headers: expect.anything(),
request: expect.anything(),
createNewCopies: false,
overwrite: false,
});

View file

@ -82,7 +82,7 @@ describe(`POST ${URL}`, () => {
const router = httpSetup.createRouter('/api/saved_objects/');
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsResolveImportErrors.mockRejectedValue(
new Error('Oh no!') // this error is intentionally swallowed so the export does not fail
new Error('Oh no!') // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
);
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerResolveImportErrorsRoute(router, { config, coreUsageData });
@ -117,7 +117,7 @@ describe(`POST ${URL}`, () => {
expect(result.body).toEqual({ success: true, successCount: 0 });
expect(savedObjectsClient.bulkCreate).not.toHaveBeenCalled(); // no objects were created
expect(coreUsageStatsClient.incrementSavedObjectsResolveImportErrors).toHaveBeenCalledWith({
headers: expect.anything(),
request: expect.anything(),
createNewCopies: false,
});
});

View file

@ -21,6 +21,9 @@ import supertest from 'supertest';
import { UnwrapPromise } from '@kbn/utility-types';
import { registerUpdateRoute } from '../update';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { CoreUsageStatsClient } from '../../../core_usage_data';
import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock';
import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock';
import { setupServer } from '../test_utils';
type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
@ -30,6 +33,7 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let coreUsageStatsClient: jest.Mocked<CoreUsageStatsClient>;
beforeEach(async () => {
const clientResponse = {
@ -47,7 +51,10 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
savedObjectsClient.update.mockResolvedValue(clientResponse);
const router = httpSetup.createRouter('/api/saved_objects/');
registerUpdateRoute(router);
coreUsageStatsClient = coreUsageStatsClientMock.create();
coreUsageStatsClient.incrementSavedObjectsUpdate.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail
const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient);
registerUpdateRoute(router, { coreUsageData });
await server.start();
});
@ -56,7 +63,7 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
await server.stop();
});
it('formats successful response', async () => {
it('formats successful response and records usage stats', async () => {
const clientResponse = {
id: 'logstash-*',
title: 'logstash-*',
@ -79,6 +86,9 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
.expect(200);
expect(result.body).toEqual(clientResponse);
expect(coreUsageStatsClient.incrementSavedObjectsUpdate).toHaveBeenCalledWith({
request: expect.anything(),
});
});
it('calls upon savedObjectClient.update', async () => {

View file

@ -83,10 +83,9 @@ export const registerResolveImportErrorsRoute = (
router.handleLegacyErrors(async (context, req, res) => {
const { createNewCopies } = req.query;
const { headers } = req;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient
.incrementSavedObjectsResolveImportErrors({ headers, createNewCopies })
.incrementSavedObjectsResolveImportErrors({ request: req, createNewCopies })
.catch(() => {});
const file = req.body.file as FileStream;

View file

@ -19,8 +19,13 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
export const registerUpdateRoute = (router: IRouter) => {
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
}
export const registerUpdateRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => {
router.put(
{
path: '/{type}/{id}',
@ -49,6 +54,9 @@ export const registerUpdateRoute = (router: IRouter) => {
const { attributes, version, references } = req.body;
const options = { version, references };
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsUpdate({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.update(type, id, attributes, options);
return res.ok({ body: result });
})

View file

@ -541,24 +541,138 @@ export interface CoreUsageDataStart {
// @internal
export interface CoreUsageStats {
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkCreate.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkGet.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsBulkUpdate.total'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsCreate.total'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsDelete.total'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.allTypesSelected.no'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.allTypesSelected.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.kibanaRequest.no'?: number;
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsExport.total'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsFind.total'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsGet.total'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.createNewCopiesEnabled.no'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.kibanaRequest.no'?: number;
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsImport.overwriteEnabled.no'?: number;
// (undocumented)
@ -570,11 +684,33 @@ export interface CoreUsageStats {
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.no'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.yes'?: number;
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsResolveImportErrors.total'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.custom.total'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.no'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.namespace.default.total'?: number;
// (undocumented)
'apiCalls.savedObjectsUpdate.total'?: number;
}
// @public (undocumented)

View file

@ -166,6 +166,7 @@ export class Server {
const metricsSetup = await this.metrics.setup({ http: httpSetup });
const coreUsageDataSetup = this.coreUsageData.setup({
http: httpSetup,
metrics: metricsSetup,
savedObjectsStartPromise: this.savedObjectsStartPromise,
});

View file

@ -115,21 +115,99 @@ export function getCoreUsageCollector(
},
},
},
// Saved Objects Client APIs
'apiCalls.savedObjectsBulkCreate.total': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsBulkGet.total': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.total': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsCreate.total': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsDelete.total': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsFind.total': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsGet.total': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsUpdate.total': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.no': { type: 'long' },
// Saved Objects Management APIs
'apiCalls.savedObjectsImport.total': { type: 'long' },
'apiCalls.savedObjectsImport.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsImport.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes': { type: 'long' },
'apiCalls.savedObjectsImport.createNewCopiesEnabled.no': { type: 'long' },
'apiCalls.savedObjectsImport.overwriteEnabled.yes': { type: 'long' },
'apiCalls.savedObjectsImport.overwriteEnabled.no': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.total': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.yes': {
type: 'long',
},
'apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.no': {
type: 'long',
},
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.yes': {
type: 'long',
},
'apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.no': {
type: 'long',
},
'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.yes': { type: 'long' },
'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.no': { type: 'long' },
'apiCalls.savedObjectsExport.total': { type: 'long' },
'apiCalls.savedObjectsExport.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsExport.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.default.total': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.default.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.custom.total': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.yes': { type: 'long' },
'apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.no': { type: 'long' },
'apiCalls.savedObjectsExport.allTypesSelected.yes': { type: 'long' },
'apiCalls.savedObjectsExport.allTypesSelected.no': { type: 'long' },
},

View file

@ -1517,13 +1517,193 @@
}
}
},
"apiCalls.savedObjectsBulkCreate.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkCreate.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkGet.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsBulkUpdate.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsCreate.total": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsCreate.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsDelete.total": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsDelete.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsFind.total": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsFind.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsGet.total": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.total": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsUpdate.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsImport.total": {
"type": "long"
},
"apiCalls.savedObjectsImport.kibanaRequest.yes": {
"apiCalls.savedObjectsImport.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsImport.kibanaRequest.no": {
"apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsImport.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsImport.createNewCopiesEnabled.yes": {
@ -1541,10 +1721,22 @@
"apiCalls.savedObjectsResolveImportErrors.total": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.kibanaRequest.yes": {
"apiCalls.savedObjectsResolveImportErrors.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.kibanaRequest.no": {
"apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.yes": {
@ -1556,10 +1748,22 @@
"apiCalls.savedObjectsExport.total": {
"type": "long"
},
"apiCalls.savedObjectsExport.kibanaRequest.yes": {
"apiCalls.savedObjectsExport.namespace.default.total": {
"type": "long"
},
"apiCalls.savedObjectsExport.kibanaRequest.no": {
"apiCalls.savedObjectsExport.namespace.default.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsExport.namespace.default.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsExport.namespace.custom.total": {
"type": "long"
},
"apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.yes": {
"type": "long"
},
"apiCalls.savedObjectsExport.namespace.custom.kibanaRequest.no": {
"type": "long"
},
"apiCalls.savedObjectsExport.allTypesSelected.yes": {

View file

@ -68,7 +68,7 @@ export default function ({ getService }) {
type: 'dashboard',
id: 'a01b2f57-fcfd-4864-b735-09e28f0d815e',
updated_at: resp.body.saved_objects[1].updated_at,
version: 'WzgsMV0=',
version: resp.body.saved_objects[1].version,
attributes: {
title: 'A great new dashboard',
},
@ -117,7 +117,7 @@ export default function ({ getService }) {
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
updated_at: resp.body.saved_objects[0].updated_at,
version: 'WzAsMV0=',
version: resp.body.saved_objects[0].version,
attributes: {
title: 'An existing visualization',
},
@ -131,7 +131,7 @@ export default function ({ getService }) {
type: 'dashboard',
id: 'a01b2f57-fcfd-4864-b735-09e28f0d815e',
updated_at: resp.body.saved_objects[1].updated_at,
version: 'WzEsMV0=',
version: resp.body.saved_objects[1].version,
attributes: {
title: 'A great new dashboard',
},

View file

@ -61,7 +61,7 @@ export default function ({ getService }) {
expect(_.omit(firstObject, ['updated_at'])).to.eql({
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
type: 'visualization',
version: 'WzgsMV0=',
version: firstObject.version,
attributes: {
title: 'An existing visualization',
},
@ -74,7 +74,7 @@ export default function ({ getService }) {
expect(_.omit(secondObject, ['updated_at'])).to.eql({
id: 'be3733a0-9efe-11e7-acb3-3dab96693fab',
type: 'dashboard',
version: 'WzksMV0=',
version: secondObject.version,
attributes: {
title: 'An existing dashboard',
},

View file

@ -53,7 +53,7 @@ export default function ({ getService }) {
type: 'visualization',
migrationVersion: resp.body.migrationVersion,
updated_at: resp.body.updated_at,
version: 'WzgsMV0=',
version: resp.body.version,
attributes: {
title: 'My favorite vis',
},
@ -100,7 +100,7 @@ export default function ({ getService }) {
type: 'visualization',
migrationVersion: resp.body.migrationVersion,
updated_at: resp.body.updated_at,
version: 'WzAsMV0=',
version: resp.body.version,
attributes: {
title: 'My favorite vis',
},

View file

@ -52,7 +52,7 @@ export default function ({ getService }) {
id: resp.body.id,
type: 'visualization',
updated_at: resp.body.updated_at,
version: 'WzgsMV0=',
version: resp.body.version,
attributes: {
title: 'My second favorite vis',
},

View file

@ -22,7 +22,7 @@ describe('UsageStatsClient', () => {
return { usageStatsClient, debugLoggerMock, repositoryMock };
};
const firstPartyRequestHeaders = { 'kbn-version': 'a', origin: 'b', referer: 'c' }; // as long as these three header fields are truthy, this will be treated like a first-party request
const firstPartyRequestHeaders = { 'kbn-version': 'a', referer: 'b' }; // as long as these two header fields are truthy, this will be treated like a first-party request
const incrementOptions = { refresh: false };
describe('#getUsageStats', () => {

View file

@ -102,7 +102,7 @@ export class UsageStatsClient {
}
function getIsKibanaRequest(headers?: Headers) {
// The presence of these three request headers gives us a good indication that this is a first-party request from the Kibana client.
// The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client.
// We can't be 100% certain, but this is a reasonable attempt.
return headers && headers['kbn-version'] && headers.origin && headers.referer;
return headers && headers['kbn-version'] && headers.referer;
}