mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Uptime] Settings public API (#163400)
This commit is contained in:
parent
8805e74f5d
commit
882e0bf81a
18 changed files with 365 additions and 185 deletions
11
docs/api/uptime-api.asciidoc
Normal file
11
docs/api/uptime-api.asciidoc
Normal file
|
@ -0,0 +1,11 @@
|
|||
[[uptime-apis]]
|
||||
== Uptime APIs
|
||||
|
||||
The following APIs are available for Uptime.
|
||||
|
||||
* <<get-settings-api, Get settings API>> to get a settings
|
||||
|
||||
* <<update-settings-api, Update settings API>> to update the attributes for existing settings
|
||||
|
||||
include::uptime/get-settings.asciidoc[leveloffset=+1]
|
||||
include::uptime/update-settings.asciidoc[leveloffset=+1]
|
39
docs/api/uptime/get-settings.asciidoc
Normal file
39
docs/api/uptime/get-settings.asciidoc
Normal file
|
@ -0,0 +1,39 @@
|
|||
[[get-settings-api]]
|
||||
== Get settings API
|
||||
++++
|
||||
<titleabbrev>Get settings</titleabbrev>
|
||||
++++
|
||||
|
||||
Retrieve uptime settings existing settings.
|
||||
|
||||
[[get-settings-api-request]]
|
||||
=== {api-request-title}
|
||||
|
||||
`GET <kibana host>:<port>/api/uptime/settings`
|
||||
|
||||
`GET <kibana host>:<port>/s/<space_id>/api/uptime/settings`
|
||||
|
||||
=== {api-prereq-title}
|
||||
|
||||
You must have `read` privileges for the *uptime* feature in *{observability}* section of the
|
||||
<<kibana-feature-privileges,{kib} feature privileges>>.
|
||||
|
||||
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": []
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
117
docs/api/uptime/update-settings.asciidoc
Normal file
117
docs/api/uptime/update-settings.asciidoc
Normal file
|
@ -0,0 +1,117 @@
|
|||
[[update-settings-api]]
|
||||
== Update settings API
|
||||
++++
|
||||
<titleabbrev>Update settings</titleabbrev>
|
||||
++++
|
||||
|
||||
Updates uptime settings attributes like heartbeatIndices, certExpirationThreshold, certAgeThreshold, defaultConnectors or defaultEmail
|
||||
|
||||
=== {api-request-title}
|
||||
|
||||
`PUT <kibana host>:<port>/api/uptime/settings`
|
||||
|
||||
`PUT <kibana host>:<port>/s/<space_id>/api/uptime/settings`
|
||||
|
||||
=== {api-prereq-title}
|
||||
|
||||
You must have `all` privileges for the *uptime* feature in *{observability}* section of the
|
||||
<<kibana-feature-privileges,{kib} feature privileges>>.
|
||||
|
||||
[[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": []
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
|
@ -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[]
|
||||
|
|
|
@ -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`,
|
||||
}
|
||||
|
|
|
@ -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&',
|
||||
|
|
|
@ -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<DynamicSettings> => {
|
||||
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<DynamicSettingsSaveResponse> => {
|
||||
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<LocationMonitor[]> => {
|
||||
|
|
|
@ -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<T>(
|
||||
apiUrl: string,
|
||||
params?: HttpFetchQuery,
|
||||
decodeType?: any,
|
||||
asResponse = false
|
||||
) {
|
||||
public async get<T>(apiUrl: string, params: Params = {}, decodeType?: any, asResponse = false) {
|
||||
const { version, ...queryParams } = params;
|
||||
const response = await this._http!.fetch<T>({
|
||||
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<T>(apiUrl: string, data?: any, decodeType?: any, params?: HttpFetchQuery) {
|
||||
public async post<T>(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) {
|
||||
const { version, ...queryParams } = params;
|
||||
|
||||
const response = await this._http!.post<T>(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<T>(apiUrl: string, data?: any, decodeType?: any) {
|
||||
public async put<T>(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) {
|
||||
const { version, ...queryParams } = params;
|
||||
|
||||
const response = await this._http!.put<T>(apiUrl, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
query: queryParams,
|
||||
version,
|
||||
});
|
||||
|
||||
return this.parseResponse(response, apiUrl, decodeType);
|
||||
|
|
|
@ -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`,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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<DynamicSettings> => {
|
||||
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<DynamicSettingsSaveResponse> => {
|
||||
return await apiService.post(apiPath, settings, DynamicSettingsSaveCodec);
|
||||
return await apiService.put(API_URLS.DYNAMIC_SETTINGS, settings, DynamicSettingsSaveCodec, {
|
||||
version: INITIAL_REST_VERSION,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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<DynamicSettingsAttributes>;
|
||||
setUptimeDynamicSettings: UMSavedObjectsQueryFn<void, DynamicSettingsAttributes>;
|
||||
setUptimeDynamicSettings: (
|
||||
client: SavedObjectsClientContract,
|
||||
attr: DynamicSettingsAttributes
|
||||
) => Promise<DynamicSettingsAttributes>;
|
||||
}
|
||||
|
||||
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<DynamicSettingsAttributes>(
|
||||
umDynamicSettings.name,
|
||||
settings,
|
||||
{
|
||||
id: settingsObjectId,
|
||||
overwrite: true,
|
||||
}
|
||||
);
|
||||
return newObj.attributes;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<DynamicSettings> = (
|
||||
|
@ -28,75 +23,56 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory<DynamicSetting
|
|||
handler: async ({ savedObjectsClient }) => {
|
||||
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<string, string> | 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<any> => {
|
||||
const decoded = DynamicSettingsCodec.decode(request.body);
|
||||
const certThresholdErrors = validateCertsValues(request.body as DynamicSettings);
|
||||
handler: async ({ savedObjectsClient, request }): Promise<DynamicSettingsAttributes> => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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-*',
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue