[Cases] Add telemetry for deprecated API (#126433)

This commit is contained in:
Christos Nasikas 2022-03-01 12:02:38 +02:00 committed by GitHub
parent 3285eb187a
commit c7f63f8f8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 140 additions and 36 deletions

View file

@ -7,7 +7,6 @@
import { schema } from '@kbn/config-schema';
import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
@ -23,20 +22,12 @@ export const getAllCommentsRoute = createCasesRoute({
case_id: schema.string(),
}),
},
options: { deprecated: true },
handler: async ({ context, request, response, logger, kibanaVersion }) => {
try {
logDeprecatedEndpoint(
logger,
request.headers,
`The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.`
);
const client = await context.cases.getCasesClient();
return response.ok({
headers: {
...getWarningHeader(kibanaVersion),
},
body: await client.attachments.getAll({
caseID: request.params.case_id,
}),

View file

@ -19,6 +19,7 @@ import { CasesRouter } from '../../types';
import { createCasesRoute } from './create_cases_route';
import { registerRoutes } from './register_routes';
import { CaseRoute } from './types';
import { extractWarningValueFromWarningHeader } from './utils';
describe('registerRoutes', () => {
let router: jest.Mocked<CasesRouter>;
@ -105,6 +106,7 @@ describe('registerRoutes', () => {
{ headers },
{ customError, badRequest }
);
return result;
};
@ -131,6 +133,26 @@ describe('registerRoutes', () => {
});
};
const initAndSimulateDeprecationEndpoint = async (headers?: Record<string, unknown>) => {
const { simulateRequest } = initApi([
...routes,
createCasesRoute({
method: 'get',
path: '/deprecated',
options: { deprecated: true },
handler: async () => response.ok(),
}),
]);
const res = await simulateRequest({
method: 'get',
path: '/deprecated',
headers,
});
return res;
};
beforeEach(() => {
jest.clearAllMocks();
router = httpServiceMock.createRouter();
@ -245,6 +267,40 @@ describe('registerRoutes', () => {
counterType: 'error',
});
});
it('increases the deprecation counters correctly', async () => {
await initAndSimulateDeprecationEndpoint();
expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
counterName: 'GET /deprecated',
counterType: 'deprecated',
});
});
});
describe('deprecation', () => {
it('logs the deprecation message if it is not a kibana request', async () => {
await initAndSimulateDeprecationEndpoint();
expect(logger.warn).toHaveBeenCalledWith('The endpoint GET /deprecated is deprecated.');
});
it('does NOT log the deprecation message if it is a kibana request', async () => {
await initAndSimulateDeprecationEndpoint({
'kbn-version': '8.2.0',
referer: 'https://example.com',
});
expect(logger.warn).not.toHaveBeenCalled();
});
it('adds the warning header', async () => {
response.ok.mockReturnValue({ status: 200, options: {} });
const res = await initAndSimulateDeprecationEndpoint();
const warningHeader = res.options.headers.warning;
const warningValue = extractWarningValueFromWarningHeader(warningHeader);
expect(warningValue).toBe('Deprecated endpoint');
});
});
describe('errors', () => {

View file

@ -6,10 +6,18 @@
*/
import { schema } from '@kbn/config-schema';
import { RouteRegistrar } from 'kibana/server';
import { Headers, RouteRegistrar } from 'kibana/server';
import { CasesRequestHandlerContext } from '../../types';
import { CaseRoute, RegisterRoutesDeps } from './types';
import { escapeHatch, getIsKibanaRequest, wrapError } from './utils';
import { RegisterRoutesDeps } from './types';
import {
escapeHatch,
getIsKibanaRequest,
getWarningHeader,
logDeprecatedEndpoint,
wrapError,
} from './utils';
const getEndpoint = (method: string, path: string): string => `${method.toUpperCase()} ${path}`;
const increaseTelemetryCounters = ({
telemetryUsageCounter,
@ -24,7 +32,7 @@ const increaseTelemetryCounters = ({
isKibanaRequest: boolean;
isError?: boolean;
}) => {
const counterName = `${method.toUpperCase()} ${path}`;
const counterName = getEndpoint(method, path);
telemetryUsageCounter.incrementCounter({
counterName,
@ -37,11 +45,36 @@ const increaseTelemetryCounters = ({
});
};
const logAndIncreaseDeprecationTelemetryCounters = ({
logger,
headers,
method,
path,
telemetryUsageCounter,
}: {
logger: RegisterRoutesDeps['logger'];
headers: Headers;
method: string;
path: string;
telemetryUsageCounter?: Exclude<RegisterRoutesDeps['telemetryUsageCounter'], undefined>;
}) => {
const endpoint = getEndpoint(method, path);
logDeprecatedEndpoint(logger, headers, `The endpoint ${endpoint} is deprecated.`);
if (telemetryUsageCounter) {
telemetryUsageCounter.incrementCounter({
counterName: endpoint,
counterType: 'deprecated',
});
}
};
export const registerRoutes = (deps: RegisterRoutesDeps) => {
const { router, routes, logger, kibanaVersion, telemetryUsageCounter } = deps;
routes.forEach((route) => {
const { method, path, params, handler } = route as CaseRoute;
const { method, path, params, options, handler } = route;
(router[method] as RouteRegistrar<typeof method, CasesRequestHandlerContext>)(
{
@ -53,6 +86,7 @@ export const registerRoutes = (deps: RegisterRoutesDeps) => {
},
},
async (context, request, response) => {
let responseHeaders = {};
const isKibanaRequest = getIsKibanaRequest(request.headers);
if (!context.cases) {
@ -60,12 +94,32 @@ export const registerRoutes = (deps: RegisterRoutesDeps) => {
}
try {
if (options?.deprecated) {
logAndIncreaseDeprecationTelemetryCounters({
telemetryUsageCounter,
logger,
path,
method,
headers: request.headers,
});
responseHeaders = {
...responseHeaders,
...getWarningHeader(kibanaVersion),
};
}
const res = await handler({ logger, context, request, response, kibanaVersion });
if (telemetryUsageCounter) {
increaseTelemetryCounters({ telemetryUsageCounter, method, path, isKibanaRequest });
}
res.options.headers = {
...res.options.headers,
...responseHeaders,
};
return res;
} catch (error) {
logger.error(error.message);

View file

@ -6,7 +6,6 @@
*/
import { CaseRoute } from '../types';
import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CasesStatusRequest } from '../../../../common/api';
import { CASE_STATUS_URL } from '../../../../common/constants';
@ -19,19 +18,11 @@ import { createCasesRoute } from '../create_cases_route';
export const getStatusRoute: CaseRoute = createCasesRoute({
method: 'get',
path: CASE_STATUS_URL,
options: { deprecated: true },
handler: async ({ context, request, response, logger, kibanaVersion }) => {
try {
logDeprecatedEndpoint(
logger,
request.headers,
`The get cases status API '${CASE_STATUS_URL}' is deprecated.`
);
const client = await context.cases.getCasesClient();
return response.ok({
headers: {
...getWarningHeader(kibanaVersion),
},
body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest),
});
} catch (error) {

View file

@ -44,5 +44,6 @@ export interface CaseRoute<P = unknown, Q = unknown, B = unknown> {
method: 'get' | 'post' | 'put' | 'delete' | 'patch';
path: string;
params?: RouteValidatorConfig<P, Q, B>;
options?: { deprecated?: boolean };
handler: (args: CaseRouteHandlerArguments<P, Q, B>) => Promise<IKibanaResponse>;
}

View file

@ -7,7 +7,6 @@
import { schema } from '@kbn/config-schema';
import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CASE_USER_ACTIONS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
@ -23,21 +22,13 @@ export const getUserActionsRoute = createCasesRoute({
case_id: schema.string(),
}),
},
options: { deprecated: true },
handler: async ({ context, request, response, logger, kibanaVersion }) => {
try {
logDeprecatedEndpoint(
logger,
request.headers,
`The get all cases user actions API '${CASE_USER_ACTIONS_URL}' is deprecated.`
);
const casesClient = await context.cases.getCasesClient();
const caseId = request.params.case_id;
return response.ok({
headers: {
...getWarningHeader(kibanaVersion),
},
body: await casesClient.userActions.getAll({ caseId }),
});
} catch (error) {

View file

@ -8,7 +8,7 @@
import { isBoom, boomify } from '@hapi/boom';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { HTTPError } from '../../common/error';
import { logDeprecatedEndpoint, wrapError } from './utils';
import { extractWarningValueFromWarningHeader, logDeprecatedEndpoint, wrapError } from './utils';
describe('Utils', () => {
describe('wrapError', () => {
@ -75,4 +75,12 @@ describe('Utils', () => {
expect(logger.warn).toHaveBeenCalledWith('test');
});
});
describe('extractWarningValueFromWarningHeader', () => {
it('extracts the warning value from a warning header correctly', () => {
expect(extractWarningValueFromWarningHeader(`299 Kibana-8.1.0 "Deprecation endpoint"`)).toBe(
'Deprecation endpoint'
);
});
});
});

View file

@ -63,3 +63,15 @@ export const logDeprecatedEndpoint = (logger: Logger, headers: Headers, msg: str
logger.warn(msg);
}
};
/**
* Extracts the warning value a warning header that is formatted according to RFC 7234.
* For example for the string 299 Kibana-8.1.0 "Deprecation endpoint", the return value is Deprecation endpoint.
*
*/
export const extractWarningValueFromWarningHeader = (warningHeader: string) => {
const firstQuote = warningHeader.indexOf('"');
const lastQuote = warningHeader.length - 1;
const warningValue = warningHeader.substring(firstQuote + 1, lastQuote);
return warningValue;
};