diff --git a/docs/api/uptime-api.asciidoc b/docs/api/uptime-api.asciidoc new file mode 100644 index 0000000000000..afc5013cb9af7 --- /dev/null +++ b/docs/api/uptime-api.asciidoc @@ -0,0 +1,11 @@ +[[uptime-apis]] +== Uptime APIs + +The following APIs are available for Uptime. + +* <> to get a settings + +* <> to update the attributes for existing settings + +include::uptime/get-settings.asciidoc[leveloffset=+1] +include::uptime/update-settings.asciidoc[leveloffset=+1] diff --git a/docs/api/uptime/get-settings.asciidoc b/docs/api/uptime/get-settings.asciidoc new file mode 100644 index 0000000000000..1b193c9f338b5 --- /dev/null +++ b/docs/api/uptime/get-settings.asciidoc @@ -0,0 +1,39 @@ +[[get-settings-api]] +== Get settings API +++++ +Get settings +++++ + +Retrieve uptime settings existing settings. + +[[get-settings-api-request]] +=== {api-request-title} + +`GET :/api/uptime/settings` + +`GET :/s//api/uptime/settings` + +=== {api-prereq-title} + +You must have `read` privileges for the *uptime* feature in *{observability}* section of the +<>. + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "heartbeatIndices": "heartbeat-8*", + "certExpirationThreshold": 30, + "certAgeThreshold": 730, + "defaultConnectors": [ + "08990f40-09c5-11ee-97ae-912b222b13d4", + "db25f830-2318-11ee-9391-6b0c030836d6" + ], + "defaultEmail": { + "to": [], + "cc": [], + "bcc": [] + } +} +-------------------------------------------------- diff --git a/docs/api/uptime/update-settings.asciidoc b/docs/api/uptime/update-settings.asciidoc new file mode 100644 index 0000000000000..c3dc34a8c150c --- /dev/null +++ b/docs/api/uptime/update-settings.asciidoc @@ -0,0 +1,117 @@ +[[update-settings-api]] +== Update settings API +++++ +Update settings +++++ + +Updates uptime settings attributes like heartbeatIndices, certExpirationThreshold, certAgeThreshold, defaultConnectors or defaultEmail + +=== {api-request-title} + +`PUT :/api/uptime/settings` + +`PUT :/s//api/uptime/settings` + +=== {api-prereq-title} + +You must have `all` privileges for the *uptime* feature in *{observability}* section of the +<>. + +[[settings-api-update-path-params]] +==== Path parameters + +`space_id`:: +(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[api-update-request-body]] +==== Request body + +A partial update is supported, provided settings keys will be merged with existing settings. + +`heartbeatIndices`:: +(Optional, string) index pattern string to be used within uptime app/alerts to query heartbeat data. Defaults to `heartbeat-*`. + + +`certExpirationThreshold`:: +(Optional, number) Number of days before a certificate expires to trigger an alert. Defaults to `30`. + +`certAgeThreshold`:: +(Optional, number) Number of days after a certificate is created to trigger an alert. Defaults to `730`. + +`defaultConnectors`:: +(Optional, array) List of connector IDs to be used as default connectors for new alerts. Defaults to `[]`. + +`defaultEmail`:: +(Optional, object) Default email configuration for new alerts. Defaults to `{"to": [], "cc": [], "bcc": []}`. + +[[settings-api-update-example]] +==== Example + +[source,sh] +-------------------------------------------------- +PUT api/uptime/settings +{ + "heartbeatIndices": "heartbeat-8*", + "certExpirationThreshold": 30, + "certAgeThreshold": 730, + "defaultConnectors": [ + "08990f40-09c5-11ee-97ae-912b222b13d4", + "db25f830-2318-11ee-9391-6b0c030836d6" + ], + "defaultEmail": { + "to": [], + "cc": [], + "bcc": [] + } +} +-------------------------------------------------- + +The API returns the following: + +[source,json] +-------------------------------------------------- +{ + "heartbeatIndices": "heartbeat-8*", + "certExpirationThreshold": 30, + "certAgeThreshold": 730, + "defaultConnectors": [ + "08990f40-09c5-11ee-97ae-912b222b13d4", + "db25f830-2318-11ee-9391-6b0c030836d6" + ], + "defaultEmail": { + "to": [], + "cc": [], + "bcc": [] + } +} +-------------------------------------------------- +[[settings-api-partial-update-example]] +==== Partial update example + +[source,sh] +-------------------------------------------------- +PUT api/uptime/settings +{ + "heartbeatIndices": "heartbeat-8*", +} +-------------------------------------------------- + +The API returns the following: + +[source,json] +-------------------------------------------------- +{ + "heartbeatIndices": "heartbeat-8*", + "certExpirationThreshold": 30, + "certAgeThreshold": 730, + "defaultConnectors": [ + "08990f40-09c5-11ee-97ae-912b222b13d4", + "db25f830-2318-11ee-9391-6b0c030836d6" + ], + "defaultEmail": { + "to": [], + "cc": [], + "bcc": [] + } +} +-------------------------------------------------- \ No newline at end of file diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index 32e4115fe59dc..4358c448f3634 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -109,3 +109,4 @@ include::{kib-repo-dir}/api/osquery-manager.asciidoc[] include::{kib-repo-dir}/api/short-urls.asciidoc[] include::{kib-repo-dir}/api/task-manager/health.asciidoc[] include::{kib-repo-dir}/api/upgrade-assistant.asciidoc[] +include::{kib-repo-dir}/api/uptime-api.asciidoc[] diff --git a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts index 02df09faae126..40ae2656e2b26 100644 --- a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts +++ b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts @@ -48,5 +48,5 @@ export enum SYNTHETICS_API_URLS { SYNTHETICS_MONITORS_PROJECT_UPDATE = '/api/synthetics/project/{projectName}/monitors/_bulk_update', SYNTHETICS_MONITORS_PROJECT_DELETE = '/api/synthetics/project/{projectName}/monitors/_bulk_delete', - DYNAMIC_SETTINGS = `/internal/uptime/dynamic_settings`, + DYNAMIC_SETTINGS = `/api/uptime/settings`, } diff --git a/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts b/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts index 59c47cf9ce20b..7bec9a7a4b389 100644 --- a/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts +++ b/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts @@ -24,7 +24,6 @@ journey('OverviewScrolling', async ({ page, params }) => { const listOfRequests: string[] = []; const expected = [ 'http://localhost:5620/internal/synthetics/service/enablement', - 'http://localhost:5620/internal/uptime/dynamic_settings', 'http://localhost:5620/internal/synthetics/monitor/filters', 'http://localhost:5620/internal/uptime/service/locations', 'http://localhost:5620/internal/synthetics/overview?sortField=status&sortOrder=asc&', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts index 46e8111c8ca9f..62bc2e4226087 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts @@ -21,20 +21,29 @@ import { import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; import { LocationMonitor } from '.'; -const apiPath = SYNTHETICS_API_URLS.DYNAMIC_SETTINGS; - interface SaveApiRequest { settings: DynamicSettings; } export const getDynamicSettings = async (): Promise => { - return await apiService.get(apiPath, undefined, DynamicSettingsCodec); + return await apiService.get( + SYNTHETICS_API_URLS.DYNAMIC_SETTINGS, + { version: '2023-10-31' }, + DynamicSettingsCodec + ); }; export const setDynamicSettings = async ({ settings, }: SaveApiRequest): Promise => { - return await apiService.post(apiPath, settings, DynamicSettingsSaveCodec); + return await apiService.put( + SYNTHETICS_API_URLS.DYNAMIC_SETTINGS, + settings, + DynamicSettingsSaveCodec, + { + version: '2023-10-31', + } + ); }; export const fetchLocationMonitors = async (): Promise => { diff --git a/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts index 9dead89809ee9..f1eb2607dd25b 100644 --- a/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts +++ b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts @@ -9,7 +9,7 @@ import { isRight } from 'fp-ts/lib/Either'; import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; import { HttpFetchQuery, HttpSetup } from '@kbn/core/public'; import { FETCH_STATUS, AddInspectorRequest } from '@kbn/observability-shared-plugin/public'; - +type Params = HttpFetchQuery & { version?: string }; class ApiService { private static instance: ApiService; private _http!: HttpSetup; @@ -59,16 +59,13 @@ class ApiService { return response; } - public async get( - apiUrl: string, - params?: HttpFetchQuery, - decodeType?: any, - asResponse = false - ) { + public async get(apiUrl: string, params: Params = {}, decodeType?: any, asResponse = false) { + const { version, ...queryParams } = params; const response = await this._http!.fetch({ path: apiUrl, - query: params, + query: queryParams, asResponse, + version, }); this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false }); @@ -76,11 +73,14 @@ class ApiService { return this.parseResponse(response, apiUrl, decodeType); } - public async post(apiUrl: string, data?: any, decodeType?: any, params?: HttpFetchQuery) { + public async post(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) { + const { version, ...queryParams } = params; + const response = await this._http!.post(apiUrl, { method: 'POST', body: JSON.stringify(data), - query: params, + query: queryParams, + version, }); this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false }); @@ -88,10 +88,14 @@ class ApiService { return this.parseResponse(response, apiUrl, decodeType); } - public async put(apiUrl: string, data?: any, decodeType?: any) { + public async put(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) { + const { version, ...queryParams } = params; + const response = await this._http!.put(apiUrl, { method: 'PUT', body: JSON.stringify(data), + query: queryParams, + version, }); return this.parseResponse(response, apiUrl, decodeType); diff --git a/x-pack/plugins/uptime/common/constants/rest_api.ts b/x-pack/plugins/uptime/common/constants/rest_api.ts index bdc9fcd04dd12..eaefdb71f7ba5 100644 --- a/x-pack/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/plugins/uptime/common/constants/rest_api.ts @@ -6,7 +6,7 @@ */ export enum API_URLS { - DYNAMIC_SETTINGS = `/internal/uptime/dynamic_settings`, + DYNAMIC_SETTINGS = `/api/uptime/settings`, INDEX_STATUS = '/internal/uptime/index_status', MONITOR_LIST = `/internal/uptime/monitor/list`, MONITOR_LOCATIONS = `/internal/uptime/monitor/locations`, diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts index d014b8b8ea6ff..19d980dfa1534 100644 --- a/x-pack/plugins/uptime/common/constants/ui.ts +++ b/x-pack/plugins/uptime/common/constants/ui.ts @@ -109,3 +109,5 @@ export const SYNTHETICS_INDEX_PATTERN = 'synthetics-*'; export const LICENSE_NOT_ACTIVE_ERROR = 'License not active'; export const LICENSE_MISSING_ERROR = 'Missing license information'; export const LICENSE_NOT_SUPPORTED_ERROR = 'License not supported'; + +export const INITIAL_REST_VERSION = '2023-10-31'; diff --git a/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts b/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts index 661fdcf46ad89..e1cb67987af5d 100644 --- a/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts @@ -12,20 +12,24 @@ import { DynamicSettingsSaveCodec, } from '../../../../common/runtime_types'; import { apiService } from './utils'; -import { API_URLS } from '../../../../common/constants'; - -const apiPath = API_URLS.DYNAMIC_SETTINGS; +import { API_URLS, INITIAL_REST_VERSION } from '../../../../common/constants'; interface SaveApiRequest { settings: DynamicSettings; } export const getDynamicSettings = async (): Promise => { - return await apiService.get(apiPath, undefined, DynamicSettingsCodec); + return await apiService.get( + API_URLS.DYNAMIC_SETTINGS, + { version: INITIAL_REST_VERSION }, + DynamicSettingsCodec + ); }; export const setDynamicSettings = async ({ settings, }: SaveApiRequest): Promise => { - return await apiService.post(apiPath, settings, DynamicSettingsSaveCodec); + return await apiService.put(API_URLS.DYNAMIC_SETTINGS, settings, DynamicSettingsSaveCodec, { + version: INITIAL_REST_VERSION, + }); }; diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts index 3108f29a97d95..99d2c717e4f94 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { SavedObjectsErrorHelpers, SavedObjectsServiceSetup } from '@kbn/core/server'; +import { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, + SavedObjectsServiceSetup, +} from '@kbn/core/server'; import { DYNAMIC_SETTINGS_DEFAULT_ATTRIBUTES } from '../../../constants/settings'; import { DynamicSettingsAttributes } from '../../../runtime_types/settings'; @@ -20,7 +24,10 @@ export const registerUptimeSavedObjects = (savedObjectsService: SavedObjectsServ export interface UMSavedObjectsAdapter { config: UptimeConfig | null; getUptimeDynamicSettings: UMSavedObjectsQueryFn; - setUptimeDynamicSettings: UMSavedObjectsQueryFn; + setUptimeDynamicSettings: ( + client: SavedObjectsClientContract, + attr: DynamicSettingsAttributes + ) => Promise; } export const savedObjectsAdapter: UMSavedObjectsAdapter = { @@ -43,10 +50,15 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = { throw getErr; } }, - setUptimeDynamicSettings: async (client, settings: DynamicSettingsAttributes | undefined) => { - await client.create(umDynamicSettings.name, settings, { - id: settingsObjectId, - overwrite: true, - }); + setUptimeDynamicSettings: async (client, settings: DynamicSettingsAttributes) => { + const newObj = await client.create( + umDynamicSettings.name, + settings, + { + id: settingsObjectId, + overwrite: true, + } + ); + return newObj.attributes; }, }; diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts deleted file mode 100644 index 117e39c0b6437..0000000000000 --- a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { validateCertsValues } from './dynamic_settings'; - -describe('dynamic settings', () => { - describe('validateCertValues', () => { - it(`doesn't allow age threshold values less than 0`, () => { - expect( - validateCertsValues({ - certAgeThreshold: -1, - certExpirationThreshold: 2, - heartbeatIndices: 'foo', - defaultConnectors: [], - }) - ).toMatchInlineSnapshot(` - Object { - "certAgeThreshold": "Value must be greater than 0.", - } - `); - }); - - it(`doesn't allow non-integer age threshold values`, () => { - expect( - validateCertsValues({ - certAgeThreshold: 10.2, - certExpirationThreshold: 2, - heartbeatIndices: 'foo', - defaultConnectors: [], - }) - ).toMatchInlineSnapshot(` - Object { - "certAgeThreshold": "Value must be an integer.", - } - `); - }); - - it(`doesn't allow expiration threshold values less than 0`, () => { - expect( - validateCertsValues({ - certAgeThreshold: 2, - certExpirationThreshold: -1, - heartbeatIndices: 'foo', - defaultConnectors: [], - }) - ).toMatchInlineSnapshot(` - Object { - "certExpirationThreshold": "Value must be greater than 0.", - } - `); - }); - - it(`doesn't allow non-integer expiration threshold values`, () => { - expect( - validateCertsValues({ - certAgeThreshold: 2, - certExpirationThreshold: 1.23, - heartbeatIndices: 'foo', - defaultConnectors: [], - }) - ).toMatchInlineSnapshot(` - Object { - "certExpirationThreshold": "Value must be an integer.", - } - `); - }); - - it('allows valid values', () => { - expect( - validateCertsValues({ - certAgeThreshold: 2, - certExpirationThreshold: 13, - heartbeatIndices: 'foo', - defaultConnectors: [], - }) - ).toBeUndefined(); - }); - }); -}); diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts index 36c2de9a37cba..e5fdcf3aa7f61 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts @@ -6,17 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -import { isRight } from 'fp-ts/lib/Either'; -import { PathReporter } from 'io-ts/lib/PathReporter'; import { UMServerLibs } from '../lib/lib'; -import { DynamicSettings, DynamicSettingsCodec } from '../../../common/runtime_types'; +import { DynamicSettings } from '../../../common/runtime_types'; import { DynamicSettingsAttributes } from '../../runtime_types/settings'; import { UMRestApiRouteFactory } from '.'; import { savedObjectsAdapter } from '../lib/saved_objects/saved_objects'; -import { - VALUE_MUST_BE_GREATER_THAN_ZERO, - VALUE_MUST_BE_AN_INTEGER, -} from '../../../common/translations'; +import { VALUE_MUST_BE_AN_INTEGER } from '../../../common/translations'; import { API_URLS } from '../../../common/constants'; export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = ( @@ -28,75 +23,56 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory { const dynamicSettingsAttributes: DynamicSettingsAttributes = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); - return { - heartbeatIndices: dynamicSettingsAttributes.heartbeatIndices, - certExpirationThreshold: dynamicSettingsAttributes.certExpirationThreshold, - certAgeThreshold: dynamicSettingsAttributes.certAgeThreshold, - defaultConnectors: dynamicSettingsAttributes.defaultConnectors, - defaultEmail: dynamicSettingsAttributes.defaultEmail, - }; + return fromAttribute(dynamicSettingsAttributes); }, }); -export const validateCertsValues = ( - settings: DynamicSettings -): Record | undefined => { - const errors: any = {}; - if (settings.certAgeThreshold <= 0) { - errors.certAgeThreshold = VALUE_MUST_BE_GREATER_THAN_ZERO; - } else if (settings.certAgeThreshold % 1) { - errors.certAgeThreshold = VALUE_MUST_BE_AN_INTEGER; - } - if (settings.certExpirationThreshold <= 0) { - errors.certExpirationThreshold = VALUE_MUST_BE_GREATER_THAN_ZERO; - } else if (settings.certExpirationThreshold % 1) { - errors.certExpirationThreshold = VALUE_MUST_BE_AN_INTEGER; - } - if (errors.certAgeThreshold || errors.certExpirationThreshold) { - return errors; +export const validateInteger = (value: number): string | undefined => { + if (value % 1) { + return VALUE_MUST_BE_AN_INTEGER; } }; +export const DynamicSettingsSchema = schema.object({ + heartbeatIndices: schema.maybe(schema.string({ minLength: 1 })), + certAgeThreshold: schema.maybe(schema.number({ min: 1, validate: validateInteger })), + certExpirationThreshold: schema.maybe(schema.number({ min: 1, validate: validateInteger })), + defaultConnectors: schema.maybe(schema.arrayOf(schema.string())), + defaultEmail: schema.maybe( + schema.object({ + to: schema.arrayOf(schema.string()), + cc: schema.maybe(schema.arrayOf(schema.string())), + bcc: schema.maybe(schema.arrayOf(schema.string())), + }) + ), +}); + export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (_libs: UMServerLibs) => ({ - method: 'POST', + method: 'PUT', path: API_URLS.DYNAMIC_SETTINGS, validate: { - body: schema.object({ - heartbeatIndices: schema.string(), - certAgeThreshold: schema.number(), - certExpirationThreshold: schema.number(), - defaultConnectors: schema.arrayOf(schema.string()), - defaultEmail: schema.maybe( - schema.object({ - to: schema.arrayOf(schema.string()), - cc: schema.maybe(schema.arrayOf(schema.string())), - bcc: schema.maybe(schema.arrayOf(schema.string())), - }) - ), - }), + body: DynamicSettingsSchema, }, writeAccess: true, - handler: async ({ savedObjectsClient, request, response }): Promise => { - const decoded = DynamicSettingsCodec.decode(request.body); - const certThresholdErrors = validateCertsValues(request.body as DynamicSettings); + handler: async ({ savedObjectsClient, request }): Promise => { + const newSettings = request.body; + const prevSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); - if (isRight(decoded) && !certThresholdErrors) { - const newSettings: DynamicSettings = decoded.right; - await savedObjectsAdapter.setUptimeDynamicSettings( - savedObjectsClient, - newSettings as DynamicSettingsAttributes - ); + const attr = await savedObjectsAdapter.setUptimeDynamicSettings(savedObjectsClient, { + ...prevSettings, + ...newSettings, + } as DynamicSettingsAttributes); - return response.ok({ - body: { - success: true, - }, - }); - } else { - const error = PathReporter.report(decoded).join(', '); - return response.badRequest({ - body: JSON.stringify(certThresholdErrors) || error, - }); - } + return fromAttribute(attr); }, }); + +const fromAttribute = (attr: DynamicSettingsAttributes) => { + return { + heartbeatIndices: attr.heartbeatIndices, + certExpirationThreshold: attr.certExpirationThreshold, + certAgeThreshold: attr.certAgeThreshold, + defaultConnectors: attr.defaultConnectors, + defaultEmail: attr.defaultEmail, + }; +}; diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts index bdffc2a44fd0d..7253c3a9096c4 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts @@ -34,8 +34,6 @@ export { uptimeRouteWrapper } from './uptime_route_wrapper'; export const legacyUptimeRestApiRoutes: UMRestApiRouteFactory[] = [ createGetPingsRoute, createGetIndexStatusRoute, - createGetDynamicSettingsRoute, - createPostDynamicSettingsRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, createMonitorListRoute, @@ -50,3 +48,8 @@ export const legacyUptimeRestApiRoutes: UMRestApiRouteFactory[] = [ createLastSuccessfulCheckRoute, createJourneyScreenshotBlocksRoute, ]; + +export const legacyUptimePublicRestApiRoutes: UMRestApiRouteFactory[] = [ + createGetDynamicSettingsRoute, + createPostDynamicSettingsRoute, +]; diff --git a/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts b/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts index cd61d40948ab1..dd01c36662e06 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts @@ -7,9 +7,16 @@ import { Logger } from '@kbn/core/server'; import { createLifecycleRuleTypeFactory, IRuleDataClient } from '@kbn/rule-registry-plugin/server'; +import { INITIAL_REST_VERSION } from '../../common/constants'; +import { DynamicSettingsSchema } from './routes/dynamic_settings'; import { UptimeRouter } from '../types'; import { uptimeRequests } from './lib/requests'; -import { createRouteWithAuth, legacyUptimeRestApiRoutes, uptimeRouteWrapper } from './routes'; +import { + createRouteWithAuth, + legacyUptimePublicRestApiRoutes, + legacyUptimeRestApiRoutes, + uptimeRouteWrapper, +} from './routes'; import { UptimeServerSetup, UptimeCorePluginsSetup } from './lib/adapters'; import { statusCheckAlertFactory } from './lib/alerts/status_check'; @@ -62,6 +69,76 @@ export const initUptimeServer = ( } }); + legacyUptimePublicRestApiRoutes.forEach((route) => { + const { method, options, handler, validate, path } = uptimeRouteWrapper( + createRouteWithAuth(libs, route), + server + ); + + const routeDefinition = { + path, + validate, + options, + }; + + switch (method) { + case 'GET': + router.versioned + .get({ + access: 'public', + path: routeDefinition.path, + options: { + tags: options?.tags, + }, + }) + .addVersion( + { + version: INITIAL_REST_VERSION, + validate: { + request: { + body: validate ? validate?.body : undefined, + }, + response: { + 200: { + body: DynamicSettingsSchema, + }, + }, + }, + }, + handler + ); + break; + case 'PUT': + router.versioned + .put({ + access: 'public', + path: routeDefinition.path, + options: { + tags: options?.tags, + }, + }) + .addVersion( + { + version: INITIAL_REST_VERSION, + validate: { + request: { + body: validate ? validate?.body : undefined, + }, + response: { + 200: { + body: DynamicSettingsSchema, + }, + }, + }, + }, + handler + ); + break; + default: + throw new Error(`Handler for method ${method} is not defined`); + } + }); + const { alerting: { registerType }, } = plugins; diff --git a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts index 21a3749fc3365..328384e8a96d1 100644 --- a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts +++ b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { it('creates rule when settings are configured', async () => { await supertest - .post(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS) + .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS) .set('kbn-xsrf', 'true') .send({ heartbeatIndices: 'heartbeat-*', @@ -76,7 +76,7 @@ export default function ({ getService }: FtrProviderContext) { it('updates rules when settings are updated', async () => { await supertest - .post(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS) + .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS) .set('kbn-xsrf', 'true') .send({ heartbeatIndices: 'heartbeat-*', diff --git a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts index 8ecdbc9b615da..987bb8c1cd64d 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts @@ -29,15 +29,24 @@ export default function ({ getService }: FtrProviderContext) { defaultConnectors: [], }; const postResponse = await supertest - .post(API_URLS.DYNAMIC_SETTINGS) + .put(API_URLS.DYNAMIC_SETTINGS) .set('kbn-xsrf', 'true') .send(newSettings); - expect(postResponse.body).to.eql({ success: true }); + expect(postResponse.body).to.eql({ + heartbeatIndices: 'myIndex1*', + certExpirationThreshold: 5, + certAgeThreshold: 15, + defaultConnectors: [], + defaultEmail: { to: [], cc: [], bcc: [] }, + }); expect(postResponse.status).to.eql(200); const getResponse = await supertest.get(API_URLS.DYNAMIC_SETTINGS); - expect(getResponse.body).to.eql(newSettings); + expect(getResponse.body).to.eql({ + ...newSettings, + defaultEmail: { to: [], cc: [], bcc: [] }, + }); expect(isRight(DynamicSettingsCodec.decode(getResponse.body))).to.be.ok(); }); });