[data] Use versioned router for remaining routes (#161919)

## Summary

Uses the versioned router for the remaining routes in the data plugin:
KQL telemetry (opt-in stats), and scripting languages list.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Lukas Olson 2023-07-18 14:40:11 -07:00 committed by GitHub
parent e96dd5cac4
commit 13372c91ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 145 additions and 68 deletions

View file

@ -14,3 +14,6 @@ export const KIBANA_USER_QUERY_LANGUAGE_KEY = 'kibana.userQueryLanguage';
export type ValueSuggestionsMethod = 'terms_enum' | 'terms_agg';
export const SAVED_QUERY_BASE_URL = '/internal/saved_query';
export const KQL_TELEMETRY_ROUTE_LATEST_VERSION = '1';
export const SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION = '1';

View file

@ -9,7 +9,13 @@
// TODO: https://github.com/elastic/kibana/issues/109904
/* eslint-disable @kbn/eslint/no_export_all */
export { DEFAULT_QUERY_LANGUAGE, KIBANA_USER_QUERY_LANGUAGE_KEY, UI_SETTINGS } from './constants';
export {
DEFAULT_QUERY_LANGUAGE,
KIBANA_USER_QUERY_LANGUAGE_KEY,
KQL_TELEMETRY_ROUTE_LATEST_VERSION,
SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION,
UI_SETTINGS,
} from './constants';
export type { ValueSuggestionsMethod } from './constants';
export { DatatableUtilitiesService } from './datatable_utilities';
export { getEsQueryConfig } from './es_query';

View file

@ -8,47 +8,64 @@
import { StartServicesAccessor, IRouter, Logger } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { KQL_TELEMETRY_ROUTE_LATEST_VERSION } from '../../common/constants';
export function registerKqlTelemetryRoute(
router: IRouter,
getStartServices: StartServicesAccessor,
logger: Logger
) {
router.post(
{
path: '/api/kibana/kql_opt_in_stats',
validate: {
body: schema.object({
opt_in: schema.boolean(),
}),
},
},
async (context, request, response) => {
const [{ savedObjects }] = await getStartServices();
const internalRepository = savedObjects.createInternalRepository();
const {
body: { opt_in: optIn },
} = request;
const counterName = optIn ? 'optInCount' : 'optOutCount';
try {
await internalRepository.incrementCounter('kql-telemetry', 'kql-telemetry', [counterName]);
} catch (error) {
logger.warn(`Unable to increment counter: ${error}`);
return response.customError({
statusCode: error.status,
body: {
message: 'Something went wrong',
attributes: {
success: false,
router.versioned
.post({
path: '/internal/kql_opt_in_stats',
access: 'internal',
})
.addVersion(
{
version: KQL_TELEMETRY_ROUTE_LATEST_VERSION,
validate: {
request: {
body: schema.object({
opt_in: schema.boolean(),
}),
},
response: {
'200': {
body: schema.object({
success: schema.boolean(),
}),
},
},
});
}
},
},
async (context, request, response) => {
const [{ savedObjects }] = await getStartServices();
const internalRepository = savedObjects.createInternalRepository();
return response.ok({ body: { success: true } });
}
);
const {
body: { opt_in: optIn },
} = request;
const counterName = optIn ? 'optInCount' : 'optOutCount';
try {
await internalRepository.incrementCounter('kql-telemetry', 'kql-telemetry', [
counterName,
]);
} catch (error) {
logger.warn(`Unable to increment counter: ${error}`);
return response.customError({
statusCode: error.status,
body: {
message: 'Something went wrong',
attributes: {
success: false,
},
},
});
}
return response.ok({ body: { success: true } });
}
);
}

View file

@ -7,14 +7,30 @@
*/
import { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION } from '../../common/constants';
export function registerScriptsRoute(router: IRouter) {
router.get(
{ path: '/api/kibana/scripts/languages', validate: false },
async (context, request, response) => {
return response.ok({
body: ['painless', 'expression'],
});
}
);
router.versioned
.get({
path: '/internal/scripts/languages',
access: 'internal',
})
.addVersion(
{
version: SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION,
validate: {
response: {
'200': {
body: schema.arrayOf(schema.string()),
},
},
},
},
async (context, request, response) => {
return response.ok({
body: ['painless', 'expression'],
});
}
);
}

View file

@ -8,6 +8,7 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { i18n } from '@kbn/i18n';
import { HttpStart, NotificationsStart } from '@kbn/core/public';
import { SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common';
export function getSupportedScriptingLanguages(): estypes.ScriptLanguage[] {
return ['painless'];
@ -21,12 +22,16 @@ export const getEnabledScriptingLanguages = (
http: HttpStart,
toasts: NotificationsStart['toasts']
) =>
http.get<estypes.ScriptLanguage[]>('/api/kibana/scripts/languages').catch(() => {
toasts.addDanger(
i18n.translate('indexPatternManagement.scriptingLanguages.errorFetchingToastDescription', {
defaultMessage: 'Error getting available scripting languages from Elasticsearch',
})
);
http
.get<estypes.ScriptLanguage[]>('/internal/scripts/languages', {
version: SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION,
})
.catch(() => {
toasts.addDanger(
i18n.translate('indexPatternManagement.scriptingLanguages.errorFetchingToastDescription', {
defaultMessage: 'Error getting available scripting languages from Elasticsearch',
})
);
return [] as estypes.ScriptLanguage[];
});
return [] as estypes.ScriptLanguage[];
});

View file

@ -28,7 +28,11 @@ import {
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE } from '@kbn/analytics';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { KIBANA_USER_QUERY_LANGUAGE_KEY, UI_SETTINGS } from '@kbn/data-plugin/common';
import {
KIBANA_USER_QUERY_LANGUAGE_KEY,
KQL_TELEMETRY_ROUTE_LATEST_VERSION,
UI_SETTINGS,
} from '@kbn/data-plugin/common';
import type { SavedQueryService, SavedQuery } from '@kbn/data-plugin/public';
import type { IUnifiedSearchPluginServices } from '../types';
import { fromUser } from './from_user';
@ -297,7 +301,8 @@ export function QueryBarMenuPanels({
};
const onSelectLanguage = (lang: string) => {
http.post('/api/kibana/kql_opt_in_stats', {
http.post('/internal/kql_opt_in_stats', {
version: KQL_TELEMETRY_ROUTE_LATEST_VERSION,
body: JSON.stringify({ opt_in: lang === 'kuery' }),
});

View file

@ -32,7 +32,11 @@ import type { Query } from '@kbn/es-query';
import { DataPublicPluginStart, getQueryLog } from '@kbn/data-plugin/public';
import { type DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { PersistedLog } from '@kbn/data-plugin/public';
import { getFieldSubtypeNested, KIBANA_USER_QUERY_LANGUAGE_KEY } from '@kbn/data-plugin/common';
import {
getFieldSubtypeNested,
KIBANA_USER_QUERY_LANGUAGE_KEY,
KQL_TELEMETRY_ROUTE_LATEST_VERSION,
} from '@kbn/data-plugin/common';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
@ -592,7 +596,8 @@ export default class QueryStringInputUI extends PureComponent<QueryStringInputPr
// Send telemetry info every time the user opts in or out of kuery
// As a result it is important this function only ever gets called in the
// UI component's change handler.
this.props.deps.http.post('/api/kibana/kql_opt_in_stats', {
this.props.deps.http.post('/internal/kql_opt_in_stats', {
version: KQL_TELEMETRY_ROUTE_LATEST_VERSION,
body: JSON.stringify({ opt_in: language === 'kuery' }),
});

View file

@ -9,6 +9,8 @@
import expect from '@kbn/expect';
import { get } from 'lodash';
import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { KQL_TELEMETRY_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
@ -30,8 +32,9 @@ export default function ({ getService }: FtrProviderContext) {
it('should increment the opt *in* counter in the .kibana_analytics/kql-telemetry document', async () => {
await supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: true })
.expect(200);
@ -48,8 +51,9 @@ export default function ({ getService }: FtrProviderContext) {
it('should increment the opt *out* counter in the .kibana_analytics/kql-telemetry document', async () => {
await supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: false })
.expect(200);
@ -66,8 +70,9 @@ export default function ({ getService }: FtrProviderContext) {
it('should report success when opt *in* is incremented successfully', () => {
return supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: true })
.expect('Content-Type', /json/)
.expect(200)
@ -78,8 +83,9 @@ export default function ({ getService }: FtrProviderContext) {
it('should report success when opt *out* is incremented successfully', () => {
return supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: false })
.expect('Content-Type', /json/)
.expect(200)
@ -91,28 +97,33 @@ export default function ({ getService }: FtrProviderContext) {
it('should only accept literal boolean values for the opt_in POST body param', function () {
return Promise.all([
supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: 'notabool' })
.expect(400),
supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: 0 })
.expect(400),
supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: null })
.expect(400),
supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: undefined })
.expect(400),
supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({})
.expect(400),
]);

View file

@ -8,13 +8,17 @@
import expect from '@kbn/expect';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common/constants';
export default function ({ getService }) {
const supertest = getService('supertest');
describe('Script Languages API', function getLanguages() {
it('should return 200 with an array of languages', () =>
supertest
.get('/api/kibana/scripts/languages')
.get('/internal/scripts/languages')
.set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION)
.expect(200)
.then((response) => {
expect(response.body).to.be.an('array');
@ -23,7 +27,8 @@ export default function ({ getService }) {
// eslint-disable-next-line jest/no-disabled-tests
it.skip('should only return langs enabled for inline scripting', () =>
supertest
.get('/api/kibana/scripts/languages')
.get('/internal/scripts/languages')
.set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION)
.expect(200)
.then((response) => {
expect(response.body).to.contain('expression');

View file

@ -6,6 +6,8 @@
*/
import expect from '@kbn/expect';
import { KQL_TELEMETRY_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
export default function ({ getService }) {
const supertestNoAuth = getService('supertestWithoutAuth');
@ -15,9 +17,10 @@ export default function ({ getService }) {
describe('no auth', () => {
it('should return 401', async () => {
return supertestNoAuth
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set('kbn-xsrf', 'much access')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: true })
.expect(401);
});
@ -26,9 +29,10 @@ export default function ({ getService }) {
describe('with auth', () => {
it('should return 200 for a successful request', async () => {
return supertest
.post('/api/kibana/kql_opt_in_stats')
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set('kbn-xsrf', 'such token, wow')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
.send({ opt_in: true })
.expect('Content-Type', /json/)
.expect(200)