diff --git a/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap b/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap index abee12da71e6b..b80765e3536c9 100644 --- a/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap +++ b/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap @@ -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", diff --git a/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts b/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts index 98141eb1163b5..c96232b9f9b48 100644 --- a/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts +++ b/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts @@ -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, { diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts index 22ca2ae38cde5..1fa1afa333e23 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts @@ -18,3 +18,5 @@ export function registerRoutes(router: InternalUiSettingsRouter) { registerSetRoute(router); registerSetManyRoute(router); } + +export { registerInternalRoutes } from './internal'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts new file mode 100644 index 0000000000000..91a81a252edd2 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts @@ -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, 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); + } + ); +} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts new file mode 100644 index 0000000000000..f9323f45e786d --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts @@ -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, + 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); + } + ); +} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts new file mode 100644 index 0000000000000..e15ded76ab6b8 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts @@ -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); +} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts new file mode 100644 index 0000000000000..a7bc0f8589599 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts @@ -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); + } + ); +} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts new file mode 100644 index 0000000000000..155f9c81120db --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts @@ -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, '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); + } + ); +} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts index 5e5d5a05345ba..bfcc5316a78a0 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts @@ -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; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts index 473e7569e2179..3352ab0ab63b0 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts @@ -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 { this.log.debug('Setting up ui settings service'); + const config = await firstValueFrom(this.config$); + this.overrides = config.overrides; savedObjects.registerType(uiSettingsType); savedObjects.registerType(uiSettingsGlobalType); - registerRoutes(http.createRouter('')); - const config = await firstValueFrom(this.config$); - this.overrides = config.overrides; + const router = http.createRouter(''); + 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, diff --git a/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts b/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts index 7be72e7b484ba..8b9277fdab8b9 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts @@ -47,7 +47,7 @@ export class KbnClientUiSettings { */ async unset(setting: string, { space }: { space?: string } = {}) { const { data } = await this.requester.request({ - 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({ - path: pathWithSpace(space)`/api/kibana/settings`, + path: pathWithSpace(space)`/internal/kibana/settings`, method: 'GET', }); diff --git a/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts b/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts index 2b6faff61bdf6..c3e595dc20958 100644 --- a/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts +++ b/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts @@ -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); diff --git a/src/core/server/integration_tests/ui_settings/doc_exists.ts b/src/core/server/integration_tests/ui_settings/doc_exists.ts index 0f4191e92ec2e..2247fd9ed68aa 100644 --- a/src/core/server/integration_tests/ui_settings/doc_exists.ts +++ b/src/core/server/integration_tests/ui_settings/doc_exists.ts @@ -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', diff --git a/src/core/server/integration_tests/ui_settings/doc_missing.ts b/src/core/server/integration_tests/ui_settings/doc_missing.ts index f48024ff6c928..b31514d4d11c8 100644 --- a/src/core/server/integration_tests/ui_settings/doc_missing.ts +++ b/src/core/server/integration_tests/ui_settings/doc_missing.ts @@ -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: { diff --git a/src/core/server/integration_tests/ui_settings/routes.test.ts b/src/core/server/integration_tests/ui_settings/routes.test.ts index 8ca1a1ad7dad1..6999731947d86 100644 --- a/src/core/server/integration_tests/ui_settings/routes.test.ts +++ b/src/core/server/integration_tests/ui_settings/routes.test.ts @@ -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; beforeAll(async () => { root = createRoot({ @@ -96,4 +96,91 @@ describe('ui settings service', () => { }); }); }); + + describe('internal routes', () => { + let root: ReturnType; + 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]' + ); + }); + }); + }); + }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 22f9090049a60..c0b9f84797f87 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -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', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx index 8a6f66238b07f..482274d27d06a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx @@ -32,7 +32,7 @@ export const SearchIndexEmptyState: React.FC = () => {

{i18n.translate('xpack.enterpriseSearch.content.newIndex.emptyState.description', { defaultMessage: - 'Data you add in Enterprise Search is called a search index and it’s searchable in both App Search and Workplace Search. Now you can use your connectors in App Search and your web crawlers in Workplace Search.', + 'Data you add in Search is called a search index and it’s searchable in both App Search and Workplace Search. Now you can use your connectors in App Search and your web crawlers in Workplace Search.', })}

} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx index 9cd4802ed2be4..6f4ff29b9e9b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx @@ -132,7 +132,7 @@ export const ConnectorConfiguration: React.FC = () => { @@ -229,7 +229,7 @@ service_type: "${index.connector.service_type || 'changeme'}" 'xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected', { defaultMessage: - 'Your connector {name} has connected to Enterprise Search successfully.', + 'Your connector {name} has connected to Search successfully.', values: { name: index.connector.name }, } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx index 75aea55e98bb5..53a6166ad34e5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx @@ -75,8 +75,7 @@ export const NativeConnectorConfigurationConfig: React.FC< title={i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.connectorConnected', { - defaultMessage: - 'Your connector {name} has connected to Enterprise Search successfully.', + defaultMessage: 'Your connector {name} has connected to Search successfully.', values: { name: nativeConnector.name }, } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx index 28a09ffa16ea3..0de71b834b6f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx @@ -135,7 +135,7 @@ export const GenerateApiKeyPanel: React.FC = () => { onChange={(event) => setOptimizedRequest(event.target.checked)} label={i18n.translate( 'xpack.enterpriseSearch.content.overview.optimizedRequest.label', - { defaultMessage: 'View Enterprise Search optimized request' } + { defaultMessage: 'View Search optimized request' } )} checked={optimizedRequest} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx index dc6dd019128d4..1dcffe443855e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx @@ -160,7 +160,7 @@ export const IngestPipelineFlyout: React.FC = ({ 'xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyConnectorText', { defaultMessage: - 'This pipeline runs automatically on all Crawler and Connector indices created through Enterprise Search.', + 'This pipeline runs automatically on all Crawler and Connector indices created through Search.', } ) )} @@ -172,7 +172,7 @@ export const IngestPipelineFlyout: React.FC = ({ {i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalIngestLinkLabel', { - defaultMessage: 'Learn more about Enterprise Search ingest pipelines', + defaultMessage: 'Learn more about Search ingest pipelines', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx index 7568481a66d5f..daac178cd8d6b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx @@ -278,8 +278,7 @@ export const ConfigurePipeline: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink', { - defaultMessage: - 'Learn more about importing and using ML models in Enterprise Search', + defaultMessage: 'Learn more about importing and using ML models in Search', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx index 16365012003b0..1ff6364ff53df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx @@ -71,7 +71,7 @@ const SharedPipelineBadge: React.FC = () => ( position="top" content={i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.shared.description', - { defaultMessage: 'This pipeline is shared across all Enterprise Search ingestion methods' } + { defaultMessage: 'This pipeline is shared across all Search ingestion methods' } )} > diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx index 3ef16e4361c8b..800c1fc0abbf1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx @@ -129,7 +129,7 @@ export const SearchIndexPipelines: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.docLink', { - defaultMessage: 'Learn more about using pipelines in Enterprise Search', + defaultMessage: 'Learn more about using pipelines in Search', } )} @@ -218,7 +218,7 @@ export const SearchIndexPipelines: React.FC = () => { 'xpack.enterpriseSearch.content.indices.pipelines.mlInferencePipelines.subtitleAPIindex', { defaultMessage: - "Inference pipelines will be run as processors from the Enterprise Search Ingest Pipeline. In order to use these pipelines on API-based indices you'll need to reference the {pipelineName} pipeline in your API requests.", + "Inference pipelines will be run as processors from the Search Ingest Pipeline. In order to use these pipelines on API-based indices you'll need to reference the {pipelineName} pipeline in your API requests.", values: { pipelineName, }, @@ -228,7 +228,7 @@ export const SearchIndexPipelines: React.FC = () => { 'xpack.enterpriseSearch.content.indices.pipelines.mlInferencePipelines.subtitle', { defaultMessage: - 'Inference pipelines will be run as processors from the Enterprise Search Ingest Pipeline', + 'Inference pipelines will be run as processors from the Search Ingest Pipeline', } ) } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx index eade81733a9eb..61ecd35d0b6b4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx @@ -62,7 +62,7 @@ export const PipelinesJSONConfigurations: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.ingestionPipelines.docLink', { - defaultMessage: 'Learn more about how Enterprise Search uses ingest pipelines', + defaultMessage: 'Learn more about how Search uses ingest pipelines', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx index d7d67ab459df2..b5db0c4f08559 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx @@ -87,7 +87,7 @@ export const SearchIndices: React.FC = () => { ? '' : hasNoIndices ? i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.emptyPageTitle', { - defaultMessage: 'Welcome to Enterprise Search', + defaultMessage: 'Welcome to Search', }) : i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.pageTitle', { defaultMessage: 'Elasticsearch indices', @@ -131,14 +131,14 @@ export const SearchIndices: React.FC = () => {

{ {i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.searchIndices.stepsTitle', { - defaultMessage: 'Build beautiful search experiences with Enterprise Search', + defaultMessage: 'Build beautiful search experiences with Search', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx index 840010d3aa219..83d5143968780 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx @@ -41,7 +41,7 @@ export const Settings: React.FC = () => { description: ( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx index 667a643c13cd4..8c7dd91b8c811 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx @@ -21,7 +21,7 @@ describe('AddContentEmptyPrompt', () => { }); it('renders', () => { - expect(wrapper.find('h2').text()).toEqual('Add content to Enterprise Search'); + expect(wrapper.find('h2').text()).toEqual('Add content to Search'); expect(wrapper.find(EuiLinkTo).prop('to')).toEqual( '/app/enterprise_search/content/search_indices/new_index' ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx index f2eeb2a29adf6..085e4c43e05c8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx @@ -51,7 +51,7 @@ export const AddContentEmptyPrompt: React.FC = ({ title, butto

{title || i18n.translate('xpack.enterpriseSearch.overview.emptyState.heading', { - defaultMessage: 'Add content to Enterprise Search', + defaultMessage: 'Add content to Search', })}

@@ -81,7 +81,7 @@ export const AddContentEmptyPrompt: React.FC = ({ title, butto {buttonLabel || i18n.translate('xpack.enterpriseSearch.overview.emptyState.buttonTitle', { - defaultMessage: 'Add content to Enterprise Search', + defaultMessage: 'Add content to Search', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx index f6d15b3157cad..6a3fec72da346 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx @@ -44,7 +44,7 @@ describe('GettingStartedSteps', () => { ...rest, })); - expect(steps[0].title).toEqual('Add your documents to Enterprise Search'); + expect(steps[0].title).toEqual('Add your documents to Search'); expect(steps[0].status).toEqual('current'); expect(steps[0].children.find(IconRow).length).toEqual(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx index 0cd7f130a9d83..d8b49cc48fca3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx @@ -29,7 +29,7 @@ export const GettingStartedSteps: React.FC = ({ step = { title: i18n.translate( 'xpack.enterpriseSearch.overview.gettingStartedSteps.addData.title', - { defaultMessage: 'Add your documents to Enterprise Search' } + { defaultMessage: 'Add your documents to Search' } ), children: ( <> @@ -39,7 +39,7 @@ export const GettingStartedSteps: React.FC = ({ step = 'xpack.enterpriseSearch.overview.gettingStartedSteps.addData.message', { defaultMessage: - 'Add your data to Enterprise Search. You can crawl website content with the Elastic web crawler, connect your existing application with Elasticsearch API endpoints, or use connectors to directly add third party content from providers like Google Drive, Microsoft Sharepoint and more.', + 'Add your data to Search. You can crawl website content with the Elastic web crawler, connect your existing application with Elasticsearch API endpoints, or use connectors to directly add third party content from providers like Google Drive, Microsoft Sharepoint and more.', } )}

diff --git a/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts b/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts index 4ba239e107f1b..fceffad66c41e 100644 --- a/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts +++ b/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts @@ -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: { diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx index b5748a16d1e43..3263b4db80c66 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx @@ -257,7 +257,7 @@ describe('useUserProfileForm', () => { ); - const overrideMsg = testWrapper.find('EuiText[data-test-subj="themeOverrideMessage"]'); + const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]'); expect(overrideMsg).toHaveLength(0); const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]'); @@ -343,8 +343,9 @@ describe('useUserProfileForm', () => { ); - const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]'); + const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]'); expect(overrideMsg).toHaveLength(1); + expect(overrideMsg.getElement().props.content).not.toEqual(''); const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]'); expect(themeMenu).toHaveLength(1); @@ -380,8 +381,9 @@ describe('useUserProfileForm', () => { ); - const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]'); + const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]'); expect(overrideMsg).toHaveLength(1); + expect(overrideMsg.getElement().props.content).not.toEqual(''); const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]'); expect(themeMenu).toHaveLength(1); diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index e9bb4b23af997..2632f73e99d07 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -26,6 +26,7 @@ import { EuiPopover, EuiSpacer, EuiText, + EuiToolTip, useEuiTheme, useGeneratedHtmlId, } from '@elastic/eui'; @@ -194,7 +195,7 @@ const UserSettingsEditor: FunctionComponent = ({ icon: string; } - const themeKeyPadMenuItem = ({ id, label, icon }: ThemeKeyPadItem) => { + const themeItem = ({ id, label, icon }: ThemeKeyPadItem) => { return ( = ({ ); }; + const themeMenu = (themeOverridden: boolean) => { + const themeKeyPadMenu = ( + + + + ), + }} + > + {themeItem({ + id: '', + label: i18n.translate('xpack.security.accountManagement.userProfile.defaultModeButton', { + defaultMessage: 'Space default', + }), + icon: 'spaces', + })} + {themeItem({ + id: 'light', + label: i18n.translate('xpack.security.accountManagement.userProfile.lightModeButton', { + defaultMessage: 'Light', + }), + icon: 'sun', + })} + {themeItem({ + id: 'dark', + label: i18n.translate('xpack.security.accountManagement.userProfile.darkModeButton', { + defaultMessage: 'Dark', + }), + icon: 'moon', + })} + + ); + return themeOverridden ? ( + + } + > + {themeKeyPadMenu} + + ) : ( + themeKeyPadMenu + ); + }; + return ( = ({ /> } > - - - - - - - {renderHelpText(isThemeOverridden)} - - } - fullWidth - > - - {themeKeyPadMenuItem({ - id: '', - label: i18n.translate( - 'xpack.security.accountManagement.userProfile.defaultModeButton', - { - defaultMessage: 'Space default', - } - ), - icon: 'spaces', - })} - {themeKeyPadMenuItem({ - id: 'light', - label: i18n.translate('xpack.security.accountManagement.userProfile.lightModeButton', { - defaultMessage: 'Light', - }), - icon: 'sun', - })} - {themeKeyPadMenuItem({ - id: 'dark', - label: i18n.translate('xpack.security.accountManagement.userProfile.darkModeButton', { - defaultMessage: 'Dark', - }), - icon: 'moon', - })} - + + {themeMenu(isThemeOverridden)} ); @@ -989,30 +1001,6 @@ export const SaveChangesBottomBar: FunctionComponent = () => { ); }; -function renderHelpText(isOverridden: boolean) { - if (isOverridden) { - return ( - - } - /> - ); - } -} - function determineIfThemeOverridden(settingsClient: IUiSettingsClient): { isThemeOverridden: boolean; isOverriddenThemeDarkMode: boolean; diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts index bec5b77de52c9..355611ba42eea 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts @@ -45,35 +45,57 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () describe('Installation of prebuilt rules package via Fleet', () => { beforeEach(() => { - cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackage'); + cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk'); + cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as( + 'installPackage' + ); waitForRulesTableToBeLoaded(); }); it('should install package from Fleet in the background', () => { /* Assert that the package in installed from Fleet by checking that /* the installSource is "registry", as opposed to "bundle" */ - cy.wait('@installPackage', { + cy.wait('@installPackageBulk', { timeout: 60000, - }).then(({ response }) => { - cy.wrap(response?.statusCode).should('eql', 200); - - const packages = response?.body.items.map(({ name, result }: BulkInstallPackageInfo) => ({ - name, - installSource: result.installSource, - })); - - expect(packages.length).to.have.greaterThan(0); - expect(packages).to.deep.include.members([ - { name: 'security_detection_engine', installSource: 'registry' }, - ]); + }).then(({ response: bulkResponse }) => { + cy.wrap(bulkResponse?.statusCode).should('eql', 200); + + const packages = bulkResponse?.body.items.map( + ({ name, result }: BulkInstallPackageInfo) => ({ + name, + installSource: result.installSource, + }) + ); + + const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name); + + // Under normal flow the package is installed via the Fleet bulk install API. + // However, for testing purposes the package can be installed via the Fleet individual install API, + // so we need to intercept and wait for that request as well. + if (!packagesBulkInstalled.includes('security_detection_engine')) { + // Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set + cy.wait('@installPackage').then(({ response }) => { + cy.wrap(response?.statusCode).should('eql', 200); + cy.wrap(response?.body) + .should('have.property', 'items') + .should('have.length.greaterThan', 0); + cy.wrap(response?.body) + .should('have.property', '_meta') + .should('have.property', 'install_source') + .should('eql', 'registry'); + }); + } else { + // Normal flow, install via the Fleet bulk install API + expect(packages.length).to.have.greaterThan(0); + expect(packages).to.deep.include.members([ + { name: 'security_detection_engine', installSource: 'registry' }, + ]); + } }); }); it('should install rules from the Fleet package when user clicks on CTA', () => { - /* Retrieve how many rules were installed from the Fleet package */ - cy.wait('@installPackage', { - timeout: 60000, - }).then(() => { + const getRulesAndAssertNumberInstalled = () => { getRuleAssets().then((response) => { const ruleIds = response.body.hits.hits.map( (hit: { _source: { ['security-rule']: Rule } }) => hit._source['security-rule'].rule_id @@ -87,6 +109,25 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () .should('be.visible') .should('have.text', `${numberOfRulesToInstall} rules installed successfully.`); }); + }; + /* Retrieve how many rules were installed from the Fleet package */ + /* See comments in test above for more details */ + cy.wait('@installPackageBulk', { + timeout: 60000, + }).then(({ response: bulkResponse }) => { + cy.wrap(bulkResponse?.statusCode).should('eql', 200); + + const packagesBulkInstalled = bulkResponse?.body.items.map( + ({ name }: { name: string }) => name + ); + + if (!packagesBulkInstalled.includes('security_detection_engine')) { + cy.wait('@installPackage').then(() => { + getRulesAndAssertNumberInstalled(); + }); + } else { + getRulesAndAssertNumberInstalled(); + } }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts index 859df059f742d..0cadb50d72fe1 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts @@ -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' }, }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts index 1a079cb43ec20..e175fe3345f80 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts @@ -170,6 +170,7 @@ export const getRuleAssets = (index: string | undefined = '.kibana_security_solu /* during e2e tests, and allow for manual installation of mock rules instead. */ export const preventPrebuiltRulesPackageInstallation = () => { cy.intercept('POST', '/api/fleet/epm/packages/_bulk*', {}); + cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*', {}); }; /** diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index d42d3ecfede31..05d08ba9702bf 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -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' }, }) diff --git a/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts b/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts index a17f22c245a4c..ccc9b92ac8298 100644 --- a/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts +++ b/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts @@ -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 } }) diff --git a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts index 0e2ebf5c9c9cc..0c31fd5d710cc 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts @@ -21,8 +21,7 @@ export default function (providerContext: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // Failing: See https://github.com/elastic/kibana/issues/161557 - describe.skip('fleet_upgrade_agent', () => { + describe('fleet_upgrade_agent', () => { skipIfNoDockerRegistry(providerContext); before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/fleet/agents'); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts index 5bce4f5b8243a..12d85ec4f9076 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts @@ -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 }, });