[Serverless] Add internal uiSettings routes and expose public routes in self-managed only (#160499)

Partially addresses https://github.com/elastic/kibana/issues/159590

## Summary

This PR adds an an internal uiSettings API that is a duplicate of the
public API and is intended for use by the browser-side uiSettings
client.

The PR also adds a config settings that is configured in serverless
context only and exposes the public uiSettings routes based on the value
of this setting (it defaults to false since we don't want to expose the
public routes in serverless).

**How to test:**

I. Verify that in serverless the internal routes are exposed but the
public ones aren't:
1. Start Es with `yarn es snapshot` and Kibana with `yarn
serverless-{mode}` where `{mode}` can be `es`, `oblt`, or `security`
(the public routes should be disabled for all projects).
2. Verify that the public endpoints are not accessible. For example,
`curl --user elastic:changeme
'http://localhost:5601/zhb/api/kibana/settings' -X 'GET'` should return
`{"statusCode":404,"error":"Not Found","message":"Not Found"}`.
3. Verify that the internal endpoints are accessible. For example, `curl
--user elastic:changeme
'http://localhost:5601/zhb/internal/kibana/settings' -X 'GET'` should
return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true},"defaultRoute":{"isOverridden":true,"userValue":"/app/elasticsearch"}}}`


II. Verify that the both public and internal routes are exposed in
self-managed:
1. Start Es with `yarn es snapshot` and Kibana with `yarn start`
2. Verify that the public endpoints are accessible. For example, `curl
--user elastic:changeme 'http://localhost:5601/zhb/api/kibana/settings'
-X 'GET'` should return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true}}}`
3. Verify that the internal endpoints are accessible. For example, `curl
--user elastic:changeme
'http://localhost:5601/zhb/internal/kibana/settings' -X 'GET'` should
return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true}}}`

III. Verify that the plugins/services that consume the internal
uiSettings endpoints work as expected in both self-managed and
serverless environment.

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Elena Stoeva 2023-07-14 15:53:17 +01:00 committed by GitHub
parent 9746a50a01
commit 41056193d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 451 additions and 51 deletions

View file

@ -3,7 +3,7 @@
exports[`#batchSet Buffers are always clear of previously buffered changes: two requests, second only sends bar, not foo 1`] = `
Array [
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -15,7 +15,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -32,7 +32,7 @@ Array [
exports[`#batchSet Overwrites previously buffered values with new values for the same key: two requests, foo=d in final 1`] = `
Array [
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -44,7 +44,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -61,7 +61,7 @@ Array [
exports[`#batchSet buffers changes while first request is in progress, sends buffered changes after first request completes: final, includes both requests 1`] = `
Array [
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -73,7 +73,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -113,7 +113,7 @@ exports[`#batchSet rejects on 500 1`] = `"Request failed with status code: 500"`
exports[`#batchSet sends a single change immediately: single change 1`] = `
Array [
Array [
"/foo/bar/api/kibana/settings",
"/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@ -130,7 +130,7 @@ Array [
exports[`#batchSetGlobal Buffers are always clear of previously buffered changes: two requests, second only sends bar, not foo 1`] = `
Array [
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -142,7 +142,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -159,7 +159,7 @@ Array [
exports[`#batchSetGlobal Overwrites previously buffered values with new values for the same key: two requests, foo=d in final 1`] = `
Array [
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -171,7 +171,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -188,7 +188,7 @@ Array [
exports[`#batchSetGlobal buffers changes while first request is in progress, sends buffered changes after first request completes: final, includes both requests 1`] = `
Array [
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -200,7 +200,7 @@ Array [
},
],
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@ -240,7 +240,7 @@ exports[`#batchSetGlobal rejects on 500 1`] = `"Request failed with status code:
exports[`#batchSetGlobal sends a single change immediately: single change 1`] = `
Array [
Array [
"/foo/bar/api/kibana/global_settings",
"/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",

View file

@ -137,7 +137,8 @@ export class UiSettingsApi {
try {
this.sendInProgress = true;
const path = scope === 'namespace' ? '/api/kibana/settings' : '/api/kibana/global_settings';
const path =
scope === 'namespace' ? '/internal/kibana/settings' : '/internal/kibana/global_settings';
changes.callback(
undefined,
await this.sendRequest('POST', path, {

View file

@ -18,3 +18,5 @@ export function registerRoutes(router: InternalUiSettingsRouter) {
registerSetRoute(router);
registerSetManyRoute(router);
}
export { registerInternalRoutes } from './internal';

View file

@ -0,0 +1,68 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { schema } from '@kbn/config-schema';
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
import type { InternalUiSettingsRouter } from '../../internal_types';
import { CannotOverrideError } from '../../ui_settings_errors';
import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
const validate = {
params: schema.object({
key: schema.string(),
}),
};
export function registerInternalDeleteRoute(router: InternalUiSettingsRouter) {
const deleteFromRequest = async (
uiSettingsClient: IUiSettingsClient,
context: InternalUiSettingsRequestHandlerContext,
request: KibanaRequest<Readonly<{} & { key: string }>, unknown, unknown, 'delete'>,
response: KibanaResponseFactory
) => {
try {
await uiSettingsClient.remove(request.params.key);
return response.ok({
body: {
settings: await uiSettingsClient.getUserProvided(),
},
});
} catch (error) {
if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
return response.customError({
body: error,
statusCode: error.output.statusCode,
});
}
if (error instanceof CannotOverrideError) {
return response.badRequest({ body: error });
}
throw error;
}
};
router.delete(
{ path: '/internal/kibana/settings/{key}', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.client;
return await deleteFromRequest(uiSettingsClient, context, request, response);
}
);
router.delete(
{ path: '/internal/kibana/global_settings/{key}', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.globalClient;
return await deleteFromRequest(uiSettingsClient, context, request, response);
}
);
}

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
import type { InternalUiSettingsRouter } from '../../internal_types';
export function registerInternalGetRoute(router: InternalUiSettingsRouter) {
const getFromRequest = async (
uiSettingsClient: IUiSettingsClient,
context: InternalUiSettingsRequestHandlerContext,
request: KibanaRequest<unknown, unknown, unknown, 'get'>,
response: KibanaResponseFactory
) => {
try {
return response.ok({
body: {
settings: await uiSettingsClient.getUserProvided(),
},
});
} catch (error) {
if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
return response.customError({
body: error,
statusCode: error.output.statusCode,
});
}
throw error;
}
};
router.get(
{ path: '/internal/kibana/settings', validate: false, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.client;
return await getFromRequest(uiSettingsClient, context, request, response);
}
);
router.get(
{ path: '/internal/kibana/global_settings', validate: false, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.globalClient;
return await getFromRequest(uiSettingsClient, context, request, response);
}
);
}

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { InternalUiSettingsRouter } from '../../internal_types';
import { registerInternalDeleteRoute } from './delete';
import { registerInternalGetRoute } from './get';
import { registerInternalSetManyRoute } from './set_many';
import { registerInternalSetRoute } from './set';
export function registerInternalRoutes(router: InternalUiSettingsRouter) {
registerInternalGetRoute(router);
registerInternalDeleteRoute(router);
registerInternalSetRoute(router);
registerInternalSetManyRoute(router);
}

View file

@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { schema, ValidationError } from '@kbn/config-schema';
import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import type {
InternalUiSettingsRequestHandlerContext,
InternalUiSettingsRouter,
} from '../../internal_types';
import { CannotOverrideError } from '../../ui_settings_errors';
const validate = {
params: schema.object({
key: schema.string(),
}),
body: schema.object({
value: schema.any(),
}),
};
export function registerInternalSetRoute(router: InternalUiSettingsRouter) {
const setFromRequest = async (
uiSettingsClient: IUiSettingsClient,
context: InternalUiSettingsRequestHandlerContext,
request: KibanaRequest<
Readonly<{} & { key: string }>,
unknown,
Readonly<{ value?: any } & {}>,
'post'
>,
response: KibanaResponseFactory
) => {
try {
const { key } = request.params;
const { value } = request.body;
await uiSettingsClient.set(key, value);
return response.ok({
body: {
settings: await uiSettingsClient.getUserProvided(),
},
});
} catch (error) {
if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
return response.customError({
body: error,
statusCode: error.output.statusCode,
});
}
if (error instanceof CannotOverrideError || error instanceof ValidationError) {
return response.badRequest({ body: error });
}
throw error;
}
};
router.post(
{ path: '/internal/kibana/settings/{key}', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.client;
return await setFromRequest(uiSettingsClient, context, request, response);
}
);
router.post(
{ path: '/internal/kibana/global_settings/{key}', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.globalClient;
return await setFromRequest(uiSettingsClient, context, request, response);
}
);
}

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { schema, ValidationError } from '@kbn/config-schema';
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import type { InternalUiSettingsRouter } from '../../internal_types';
import { CannotOverrideError } from '../../ui_settings_errors';
import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
const validate = {
body: schema.object({
changes: schema.object({}, { unknowns: 'allow' }),
}),
};
export function registerInternalSetManyRoute(router: InternalUiSettingsRouter) {
const setManyFromRequest = async (
uiSettingsClient: IUiSettingsClient,
context: InternalUiSettingsRequestHandlerContext,
request: KibanaRequest<unknown, unknown, Readonly<{} & { changes?: any & {} }>, 'post'>,
response: KibanaResponseFactory
) => {
try {
const { changes } = request.body;
await uiSettingsClient.setMany(changes);
return response.ok({
body: {
settings: await uiSettingsClient.getUserProvided(),
},
});
} catch (error) {
if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
return response.customError({
body: error,
statusCode: error.output.statusCode,
});
}
if (error instanceof CannotOverrideError || error instanceof ValidationError) {
return response.badRequest({ body: error });
}
throw error;
}
};
router.post(
{ path: '/internal/kibana/settings', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.client;
return await setManyFromRequest(uiSettingsClient, context, request, response);
}
);
router.post(
{ path: '/internal/kibana/global_settings', validate, options: { access: 'internal' } },
async (context, request, response) => {
const uiSettingsClient = (await context.core).uiSettings.globalClient;
return await setManyFromRequest(uiSettingsClient, context, request, response);
}
);
}

View file

@ -17,6 +17,12 @@ const deprecations: ConfigDeprecationProvider = ({ unused, renameFromRoot }) =>
const configSchema = schema.object({
overrides: schema.object({}, { unknowns: 'allow' }),
publicApiEnabled: schema.conditional(
schema.contextRef('serverless'),
true,
schema.boolean({ defaultValue: false }),
schema.never()
),
});
export type UiSettingsConfigType = TypeOf<typeof configSchema>;

View file

@ -25,7 +25,7 @@ import type {
} from './types';
import type { InternalUiSettingsRequestHandlerContext } from './internal_types';
import { uiSettingsType, uiSettingsGlobalType } from './saved_objects';
import { registerRoutes } from './routes';
import { registerRoutes, registerInternalRoutes } from './routes';
import { getCoreSettings } from './settings';
import { UiSettingsDefaultsClient } from './clients/ui_settings_defaults_client';
@ -78,12 +78,18 @@ export class UiSettingsService
public async setup({ http, savedObjects }: SetupDeps): Promise<InternalUiSettingsServiceSetup> {
this.log.debug('Setting up ui settings service');
savedObjects.registerType(uiSettingsType);
savedObjects.registerType(uiSettingsGlobalType);
registerRoutes(http.createRouter<InternalUiSettingsRequestHandlerContext>(''));
const config = await firstValueFrom(this.config$);
this.overrides = config.overrides;
savedObjects.registerType(uiSettingsType);
savedObjects.registerType(uiSettingsGlobalType);
const router = http.createRouter<InternalUiSettingsRequestHandlerContext>('');
registerInternalRoutes(router);
// Register public routes by default unless the publicApiEnabled config setting is set to false
if (!config.hasOwnProperty('publicApiEnabled') || config.publicApiEnabled === true) {
registerRoutes(router);
}
return {
register: this.register,

View file

@ -47,7 +47,7 @@ export class KbnClientUiSettings {
*/
async unset(setting: string, { space }: { space?: string } = {}) {
const { data } = await this.requester.request<any>({
path: pathWithSpace(space)`/api/kibana/settings/${setting}`,
path: pathWithSpace(space)`/internal/kibana/settings/${setting}`,
method: 'DELETE',
});
return data;
@ -76,7 +76,7 @@ export class KbnClientUiSettings {
await this.requester.request({
method: 'POST',
path: pathWithSpace(space)`/api/kibana/settings`,
path: pathWithSpace(space)`/internal/kibana/settings`,
body: { changes },
retries,
});
@ -89,7 +89,7 @@ export class KbnClientUiSettings {
this.log.debug('applying update to kibana config: %j', updates);
await this.requester.request({
path: pathWithSpace(space)`/api/kibana/settings`,
path: pathWithSpace(space)`/internal/kibana/settings`,
method: 'POST',
body: {
changes: updates,
@ -100,7 +100,7 @@ export class KbnClientUiSettings {
private async getAll({ space }: { space?: string } = {}) {
const { data } = await this.requester.request<UiSettingsApiResponse>({
path: pathWithSpace(space)`/api/kibana/settings`,
path: pathWithSpace(space)`/internal/kibana/settings`,
method: 'GET',
});

View file

@ -58,7 +58,7 @@ describe('default route provider', () => {
for (const url of invalidRoutes) {
await request
.post(root, '/api/kibana/settings/defaultRoute')
.post(root, '/internal/kibana/settings/defaultRoute')
.send({ value: url })
.expect(400);
}
@ -72,7 +72,7 @@ describe('default route provider', () => {
it('consumes valid values', async function () {
await request
.post(root, '/api/kibana/settings/defaultRoute')
.post(root, '/internal/kibana/settings/defaultRoute')
.send({ value: '/valid' })
.expect(200);

View file

@ -50,7 +50,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
},
});
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
const { body } = await supertest('get', '/internal/kibana/settings').expect(200);
expect(body).toMatchObject({
settings: {
@ -75,7 +75,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
const { body } = await supertest('post', '/internal/kibana/settings/defaultIndex')
.send({
value: defaultIndex,
})
@ -100,7 +100,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('delete', '/api/kibana/settings/foo')
const { body } = await supertest('delete', '/internal/kibana/settings/foo')
.send({
value: 'baz',
})
@ -119,7 +119,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { supertest } = await setup();
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/settings')
const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: {
defaultIndex,
@ -146,7 +146,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('post', '/api/kibana/settings')
const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: {
foo: 'baz',
@ -172,7 +172,9 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
expect(await uiSettings.get('defaultIndex')).toBe(defaultIndex);
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
const { body } = await supertest('delete', '/internal/kibana/settings/defaultIndex').expect(
200
);
expect(body).toMatchObject({
settings: {
@ -189,7 +191,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if deleting overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('delete', '/api/kibana/settings/foo').expect(400);
const { body } = await supertest('delete', '/internal/kibana/settings/foo').expect(400);
expect(body).toEqual({
error: 'Bad Request',
@ -210,7 +212,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
},
});
const { body } = await supertest('get', '/api/kibana/global_settings').expect(200);
const { body } = await supertest('get', '/internal/kibana/global_settings').expect(200);
expect(body).toMatchObject({
settings: {
@ -231,7 +233,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/global_settings/defaultIndex')
const { body } = await supertest('post', '/internal/kibana/global_settings/defaultIndex')
.send({
value: defaultIndex,
})
@ -253,7 +255,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('delete', '/api/kibana/global_settings/foo')
const { body } = await supertest('delete', '/internal/kibana/global_settings/foo')
.send({
value: 'baz',
})
@ -272,7 +274,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { supertest } = await setup();
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/global_settings')
const { body } = await supertest('post', '/internal/kibana/global_settings')
.send({
changes: {
defaultIndex,
@ -296,7 +298,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('post', '/api/kibana/global_settings')
const { body } = await supertest('post', '/internal/kibana/global_settings')
.send({
changes: {
foo: 'baz',
@ -324,7 +326,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { body } = await supertest(
'delete',
'/api/kibana/global_settings/defaultIndex'
'/internal/kibana/global_settings/defaultIndex'
).expect(200);
expect(body).toMatchObject({
@ -339,7 +341,9 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if deleting overridden value', async () => {
const { supertest } = await setup();
const { body } = await supertest('delete', '/api/kibana/global_settings/foo').expect(400);
const { body } = await supertest('delete', '/internal/kibana/global_settings/foo').expect(
400
);
expect(body).toEqual({
error: 'Bad Request',

View file

@ -26,7 +26,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
it('creates doc, returns a 200 with settings', async () => {
const { supertest } = getServices();
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
const { body } = await supertest('get', '/internal/kibana/settings').expect(200);
expect(body).toMatchObject({
settings: {
@ -47,7 +47,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
const { supertest } = getServices();
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
const { body } = await supertest('post', '/internal/kibana/settings/defaultIndex')
.send({
value: defaultIndex,
})
@ -76,7 +76,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
const { body } = await supertest('post', '/api/kibana/settings')
const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: { defaultIndex },
})
@ -103,7 +103,9 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
it('creates doc, returns a 200 with just buildNum', async () => {
const { supertest } = getServices();
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
const { body } = await supertest('delete', '/internal/kibana/settings/defaultIndex').expect(
200
);
expect(body).toMatchObject({
settings: {

View file

@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema';
import { createRoot, request } from '@kbn/core-test-helpers-kbn-server';
describe('ui settings service', () => {
describe('routes', () => {
describe('public routes', () => {
let root: ReturnType<typeof createRoot>;
beforeAll(async () => {
root = createRoot({
@ -96,4 +96,91 @@ describe('ui settings service', () => {
});
});
});
describe('internal routes', () => {
let root: ReturnType<typeof createRoot>;
beforeAll(async () => {
root = createRoot({
plugins: { initialize: false },
elasticsearch: { skipStartupConnectionCheck: true },
});
await root.preboot();
const { uiSettings } = await root.setup();
uiSettings.register({
custom: {
value: '42',
schema: schema.string(),
},
});
// global uiSettings have to be registerd to be set
uiSettings.registerGlobal({
custom: {
value: '42',
schema: schema.string(),
},
});
uiSettings.registerGlobal({
foo: {
value: 'foo',
schema: schema.string(),
},
});
await root.start();
});
afterAll(async () => await root.shutdown());
describe('set', () => {
it('validates value', async () => {
const response = await request
.post(root, '/internal/kibana/settings/custom')
.send({ value: 100 })
.expect(400);
expect(response.body.message).toBe(
'[validation [custom]]: expected value of type [string] but got [number]'
);
});
});
describe('set many', () => {
it('validates value', async () => {
const response = await request
.post(root, '/internal/kibana/settings')
.send({ changes: { custom: 100, foo: 'bar' } })
.expect(400);
expect(response.body.message).toBe(
'[validation [custom]]: expected value of type [string] but got [number]'
);
});
});
describe('global', () => {
describe('set', () => {
it('validates value', async () => {
const response = await request
.post(root, '/internal/kibana/global_settings/custom')
.send({ value: 100 })
.expect(400);
expect(response.body.message).toBe(
'[validation [custom]]: expected value of type [string] but got [number]'
);
});
});
describe('set many', () => {
it('validates value', async () => {
const response = await request
.post(root, '/internal/kibana/global_settings')
.send({ changes: { custom: 100, foo: 'bar' } })
.expect(400);
expect(response.body.message).toBe(
'[validation [custom]]: expected value of type [string] but got [number]'
);
});
});
});
});
});

View file

@ -122,7 +122,7 @@ Cypress.Commands.add(
cy.request({
log: false,
method: 'POST',
url: `${kibanaUrl}/api/kibana/settings`,
url: `${kibanaUrl}/internal/kibana/settings`,
body: { changes: settings },
headers: {
'kbn-xsrf': 'e2e_test',

View file

@ -9,7 +9,7 @@
export function setUISettings(settingsKey: string, settingsValue: any) {
cy.request({
method: 'POST',
url: '/api/kibana/settings',
url: '/internal/kibana/settings',
headers: { 'kbn-xsrf': 'xx' },
body: {
changes: {

View file

@ -10,7 +10,7 @@ import { rootRequest } from '../common';
const kibanaSettings = (body: Cypress.RequestBody) => {
rootRequest({
method: 'POST',
url: 'api/kibana/settings',
url: 'internal/kibana/settings',
body,
headers: { 'kbn-xsrf': 'cypress-creds' },
});

View file

@ -471,7 +471,7 @@ export const setKibanaTimezoneToUTC = () =>
cy
.request({
method: 'POST',
url: 'api/kibana/settings',
url: 'internal/kibana/settings',
body: { changes: { 'dateFormat:tz': 'UTC' } },
headers: { 'kbn-xsrf': 'set-kibana-timezone-utc' },
})

View file

@ -52,7 +52,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
const basePath = spaceId ? `/s/${spaceId}` : '';
return await supertest
.post(`${basePath}/api/kibana/settings`)
.post(`${basePath}/internal/kibana/settings`)
.auth(username, password)
.set('kbn-xsrf', 'foo')
.send({ changes: { [CSV_QUOTE_VALUES_SETTING]: null } })

View file

@ -21,7 +21,7 @@ export default function ({ getService }: FtrProviderContext) {
const setSpaceConfig = async (spaceId: string, settings: object) => {
return await kibanaServer.request({
path: `/s/${spaceId}/api/kibana/settings`,
path: `/s/${spaceId}/internal/kibana/settings`,
method: 'POST',
body: { changes: settings },
});