mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Synthetics] Refactor: Create monitor configs repository !! (#202325)
## Summary Create monitor configs repository around monitor saved object to make sure all operations are performed from same class. This will be helpful when we create a new saved object to support multiple-spaces !! ### Testing All unit tests, api tests passing should be more than enough !! --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
b0ef1e6365
commit
9100170e29
34 changed files with 959 additions and 434 deletions
|
@ -12,7 +12,6 @@ import { mockEncryptedSO } from '../../synthetics_service/utils/mocks';
|
|||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
import { SyntheticsService } from '../../synthetics_service/synthetics_service';
|
||||
import * as monitorUtils from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import * as locationsUtils from '../../synthetics_service/get_all_locations';
|
||||
import type { PublicLocation } from '../../../common/runtime_types';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
|
@ -82,10 +81,11 @@ describe('StatusRuleExecutor', () => {
|
|||
name: 'test',
|
||||
},
|
||||
} as any);
|
||||
const configRepo = statusRule.monitorConfigRepository;
|
||||
|
||||
describe('DefaultRule', () => {
|
||||
it('should only query enabled monitors', async () => {
|
||||
const spy = jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue([]);
|
||||
const spy = jest.spyOn(configRepo, 'getAll').mockResolvedValue([]);
|
||||
|
||||
const { downConfigs, staleDownConfigs } = await statusRule.getDownChecks({});
|
||||
|
||||
|
@ -94,12 +94,11 @@ describe('StatusRuleExecutor', () => {
|
|||
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
filter: 'synthetics-monitor.attributes.alert.status.enabled: true',
|
||||
soClient,
|
||||
});
|
||||
});
|
||||
|
||||
it('marks deleted configs as expected', async () => {
|
||||
jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue(testMonitors);
|
||||
jest.spyOn(configRepo, 'getAll').mockResolvedValue(testMonitors);
|
||||
|
||||
const { downConfigs } = await statusRule.getDownChecks({});
|
||||
|
||||
|
@ -175,7 +174,7 @@ describe('StatusRuleExecutor', () => {
|
|||
});
|
||||
|
||||
it('does not mark deleted config when monitor does not contain location label', async () => {
|
||||
jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue([
|
||||
jest.spyOn(configRepo, 'getAll').mockResolvedValue([
|
||||
{
|
||||
...testMonitors[0],
|
||||
attributes: {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Logger } from '@kbn/core/server';
|
|||
import { intersection, isEmpty, uniq } from 'lodash';
|
||||
import { getAlertDetailsUrl } from '@kbn/observability-plugin/common';
|
||||
import { SyntheticsMonitorStatusRuleParams as StatusRuleParams } from '@kbn/response-ops-rule-params/synthetics_monitor_status';
|
||||
import { MonitorConfigRepository } from '../../services/monitor_config_repository';
|
||||
import {
|
||||
AlertOverviewStatus,
|
||||
AlertStatusConfigs,
|
||||
|
@ -38,10 +39,7 @@ import { queryMonitorStatusAlert } from './queries/query_monitor_status_alert';
|
|||
import { parseArrayFilters } from '../../routes/common';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
import { SyntheticsEsClient } from '../../lib';
|
||||
import {
|
||||
getAllMonitors,
|
||||
processMonitors,
|
||||
} from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { processMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { getConditionType } from '../../../common/rules/status_rule';
|
||||
import { ConfigKey, EncryptedSyntheticsMonitorAttributes } from '../../../common/runtime_types';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
|
@ -65,6 +63,7 @@ export class StatusRuleExecutor {
|
|||
options: StatusRuleExecutorOptions;
|
||||
logger: Logger;
|
||||
ruleName: string;
|
||||
monitorConfigRepository: MonitorConfigRepository;
|
||||
|
||||
constructor(
|
||||
esClient: SyntheticsEsClient,
|
||||
|
@ -80,6 +79,10 @@ export class StatusRuleExecutor {
|
|||
this.params = params;
|
||||
this.soClient = savedObjectsClient;
|
||||
this.esClient = esClient;
|
||||
this.monitorConfigRepository = new MonitorConfigRepository(
|
||||
savedObjectsClient,
|
||||
server.encryptedSavedObjects.getClient()
|
||||
);
|
||||
this.server = server;
|
||||
this.syntheticsMonitorClient = syntheticsMonitorClient;
|
||||
this.hasCustomCondition = !isEmpty(this.params);
|
||||
|
@ -125,8 +128,7 @@ export class StatusRuleExecutor {
|
|||
projects: this.params?.projects,
|
||||
});
|
||||
|
||||
this.monitors = await getAllMonitors({
|
||||
soClient: this.soClient,
|
||||
this.monitors = await this.monitorConfigRepository.getAll({
|
||||
filter: filtersStr,
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { mockEncryptedSO } from '../../synthetics_service/utils/mocks';
|
|||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
import { SyntheticsService } from '../../synthetics_service/synthetics_service';
|
||||
import * as monitorUtils from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import * as locationsUtils from '../../synthetics_service/get_all_locations';
|
||||
import type { PublicLocation } from '../../../common/runtime_types';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
|
@ -60,7 +59,6 @@ describe('tlsRuleExecutor', () => {
|
|||
const monitorClient = new SyntheticsMonitorClient(syntheticsService, serverMock);
|
||||
|
||||
it('should only query enabled monitors', async () => {
|
||||
const spy = jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue([]);
|
||||
const tlsRule = new TLSRuleExecutor(
|
||||
moment().toDate(),
|
||||
{},
|
||||
|
@ -69,6 +67,8 @@ describe('tlsRuleExecutor', () => {
|
|||
serverMock,
|
||||
monitorClient
|
||||
);
|
||||
const configRepo = tlsRule.monitorConfigRepository;
|
||||
const spy = jest.spyOn(configRepo, 'getAll').mockResolvedValue([]);
|
||||
|
||||
const { certs } = await tlsRule.getExpiredCertificates();
|
||||
|
||||
|
@ -77,7 +77,6 @@ describe('tlsRuleExecutor', () => {
|
|||
expect(spy).toHaveBeenCalledWith({
|
||||
filter:
|
||||
'synthetics-monitor.attributes.alert.tls.enabled: true and (synthetics-monitor.attributes.type: http or synthetics-monitor.attributes.type: tcp)',
|
||||
soClient,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,16 +12,14 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
|||
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { TLSRuleParams } from '@kbn/response-ops-rule-params/synthetics_tls';
|
||||
import moment from 'moment';
|
||||
import { MonitorConfigRepository } from '../../services/monitor_config_repository';
|
||||
import { FINAL_SUMMARY_FILTER } from '../../../common/constants/client_defaults';
|
||||
import { formatFilterString } from '../common';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
import { getSyntheticsCerts } from '../../queries/get_certs';
|
||||
import { savedObjectsAdapter } from '../../saved_objects';
|
||||
import { DYNAMIC_SETTINGS_DEFAULTS, SYNTHETICS_INDEX_PATTERN } from '../../../common/constants';
|
||||
import {
|
||||
getAllMonitors,
|
||||
processMonitors,
|
||||
} from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { processMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import {
|
||||
CertResult,
|
||||
ConfigKey,
|
||||
|
@ -41,6 +39,7 @@ export class TLSRuleExecutor {
|
|||
server: SyntheticsServerSetup;
|
||||
syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
monitors: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>> = [];
|
||||
monitorConfigRepository: MonitorConfigRepository;
|
||||
|
||||
constructor(
|
||||
previousStartedAt: Date | null,
|
||||
|
@ -58,12 +57,15 @@ export class TLSRuleExecutor {
|
|||
});
|
||||
this.server = server;
|
||||
this.syntheticsMonitorClient = syntheticsMonitorClient;
|
||||
this.monitorConfigRepository = new MonitorConfigRepository(
|
||||
soClient,
|
||||
server.encryptedSavedObjects.getClient()
|
||||
);
|
||||
}
|
||||
|
||||
async getMonitors() {
|
||||
const HTTP_OR_TCP = `${monitorAttributes}.${ConfigKey.MONITOR_TYPE}: http or ${monitorAttributes}.${ConfigKey.MONITOR_TYPE}: tcp`;
|
||||
this.monitors = await getAllMonitors({
|
||||
soClient: this.soClient,
|
||||
this.monitors = await this.monitorConfigRepository.getAll({
|
||||
filter: `${monitorAttributes}.${AlertConfigKey.TLS_ENABLED}: true and (${HTTP_OR_TCP})`,
|
||||
});
|
||||
|
||||
|
|
|
@ -4,36 +4,3 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { syntheticsMonitorType } from '../../common/types/saved_objects';
|
||||
import {
|
||||
SyntheticsMonitorWithSecretsAttributes,
|
||||
SyntheticsMonitor,
|
||||
} from '../../common/runtime_types';
|
||||
import { normalizeSecrets } from '../synthetics_service/utils/secrets';
|
||||
|
||||
export const getSyntheticsMonitor = async ({
|
||||
monitorId,
|
||||
encryptedSavedObjectsClient,
|
||||
spaceId,
|
||||
}: {
|
||||
monitorId: string;
|
||||
spaceId: string;
|
||||
encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
|
||||
}): Promise<SavedObject<SyntheticsMonitor>> => {
|
||||
try {
|
||||
const decryptedMonitor =
|
||||
await encryptedSavedObjectsClient.getDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
|
||||
syntheticsMonitorType,
|
||||
monitorId,
|
||||
{
|
||||
namespace: spaceId,
|
||||
}
|
||||
);
|
||||
return normalizeSecrets(decryptedMonitor);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { IKibanaResponse } from '@kbn/core-http-server';
|
||||
import { getJourneyScreenshot, ScreenshotReturnTypesUnion } from './get_journey_screenshot';
|
||||
import { isRefResult, RefResult } from '../../common/runtime_types';
|
||||
import { RouteContext, UptimeRouteContext } from '../routes/types';
|
||||
import { RouteContext } from '../routes/types';
|
||||
|
||||
export interface ClientContract {
|
||||
screenshotRef: RefResult;
|
||||
|
@ -25,7 +25,7 @@ export const journeyScreenshotHandler = async ({
|
|||
response,
|
||||
request,
|
||||
syntheticsEsClient,
|
||||
}: RouteContext | UptimeRouteContext): Promise<IKibanaResponse<ClientContract>> => {
|
||||
}: RouteContext): Promise<IKibanaResponse<ClientContract>> => {
|
||||
const { checkGroup, stepIndex } = request.params;
|
||||
|
||||
const result: ScreenshotReturnTypesUnion | null = await getJourneyScreenshot({
|
||||
|
|
|
@ -8,25 +8,31 @@
|
|||
import * as getAllMonitors from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import * as getCerts from '../../queries/get_certs';
|
||||
import { getSyntheticsCertsRoute } from './get_certificates';
|
||||
import { MonitorConfigRepository } from '../../services/monitor_config_repository';
|
||||
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
|
||||
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
|
||||
|
||||
describe('getSyntheticsCertsRoute', () => {
|
||||
let getMonitorsSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
getMonitorsSpy = jest.spyOn(getAllMonitors, 'getAllMonitors').mockReturnValue([] as any);
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createStart().getClient();
|
||||
|
||||
const mockMonitorConfigRepository = new MonitorConfigRepository(
|
||||
soClient,
|
||||
encryptedSavedObjectsClient
|
||||
);
|
||||
|
||||
it('returns empty set when no monitors are found', async () => {
|
||||
const route = getSyntheticsCertsRoute();
|
||||
mockMonitorConfigRepository.getAll = jest.fn().mockReturnValue([]);
|
||||
expect(
|
||||
await route.handler({
|
||||
// @ts-expect-error partial implementation for testing
|
||||
request: { query: {} },
|
||||
// @ts-expect-error partial implementation for testing
|
||||
syntheticsEsClient: jest.fn(),
|
||||
savedObjectClient: jest.fn(),
|
||||
savedObjectClient: soClient,
|
||||
monitorConfigRepository: mockMonitorConfigRepository,
|
||||
})
|
||||
).toEqual({
|
||||
data: {
|
||||
|
@ -34,7 +40,7 @@ describe('getSyntheticsCertsRoute', () => {
|
|||
total: 0,
|
||||
},
|
||||
});
|
||||
expect(getMonitorsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(mockMonitorConfigRepository.getAll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns cert data when monitors are found', async () => {
|
||||
|
@ -78,15 +84,17 @@ describe('getSyntheticsCertsRoute', () => {
|
|||
// @ts-expect-error partial implementation for testing
|
||||
.mockReturnValue(getCertsResult);
|
||||
const route = getSyntheticsCertsRoute();
|
||||
getMonitorsSpy.mockReturnValue(getMonitorsResult);
|
||||
const getAll = jest.fn().mockReturnValue(getMonitorsResult);
|
||||
const result = await route.handler({
|
||||
// @ts-expect-error partial implementation for testing
|
||||
request: { query: {} },
|
||||
// @ts-expect-error partial implementation for testing
|
||||
syntheticsEsClient: jest.fn(),
|
||||
savedObjectClient: jest.fn(),
|
||||
// @ts-expect-error partial implementation for testing
|
||||
monitorConfigRepository: { getAll },
|
||||
});
|
||||
expect(getMonitorsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getAll).toHaveBeenCalledTimes(1);
|
||||
expect(processMonitorsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(processMonitorsSpy).toHaveBeenCalledWith(getMonitorsResult);
|
||||
expect(getSyntheticsCertsSpy).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SyntheticsRestApiRouteFactory } from '../types';
|
||||
import {
|
||||
getAllMonitors,
|
||||
processMonitors,
|
||||
} from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { processMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { monitorAttributes } from '../../../common/types/saved_objects';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
import { CertResult, GetCertsParams } from '../../../common/runtime_types';
|
||||
|
@ -34,11 +31,10 @@ export const getSyntheticsCertsRoute: SyntheticsRestApiRouteFactory<
|
|||
to: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
handler: async ({ request, syntheticsEsClient, savedObjectsClient }) => {
|
||||
handler: async ({ request, syntheticsEsClient, monitorConfigRepository }) => {
|
||||
const queryParams = request.query;
|
||||
|
||||
const monitors = await getAllMonitors({
|
||||
soClient: savedObjectsClient,
|
||||
const monitors = await monitorConfigRepository.getAll({
|
||||
filter: `${monitorAttributes}.${ConfigKey.ENABLED}: true`,
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import { MonitorSortFieldSchema } from '../../common/runtime_types/monitor_manag
|
|||
import { getAllLocations } from '../synthetics_service/get_all_locations';
|
||||
import { EncryptedSyntheticsMonitorAttributes } from '../../common/runtime_types';
|
||||
import { PrivateLocation, ServiceLocation } from '../../common/runtime_types';
|
||||
import { monitorAttributes, syntheticsMonitorType } from '../../common/types/saved_objects';
|
||||
import { monitorAttributes } from '../../common/types/saved_objects';
|
||||
|
||||
const StringOrArraySchema = schema.maybe(
|
||||
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
|
||||
|
@ -82,30 +82,13 @@ export const getMonitors = async (
|
|||
sortField,
|
||||
sortOrder,
|
||||
query,
|
||||
tags,
|
||||
monitorTypes,
|
||||
locations,
|
||||
filter = '',
|
||||
searchAfter,
|
||||
projects,
|
||||
schedules,
|
||||
monitorQueryIds,
|
||||
showFromAllSpaces,
|
||||
} = context.request.query;
|
||||
|
||||
const { filtersStr } = await getMonitorFilters({
|
||||
filter,
|
||||
monitorTypes,
|
||||
tags,
|
||||
locations,
|
||||
projects,
|
||||
schedules,
|
||||
monitorQueryIds,
|
||||
context,
|
||||
});
|
||||
const { filtersStr } = await getMonitorFilters(context);
|
||||
|
||||
return context.savedObjectsClient.find({
|
||||
type: syntheticsMonitorType,
|
||||
return context.monitorConfigRepository.find({
|
||||
perPage,
|
||||
page,
|
||||
sortField: parseMappingKey(sortField),
|
||||
|
@ -129,16 +112,26 @@ interface Filters {
|
|||
monitorQueryIds?: string | string[];
|
||||
}
|
||||
|
||||
export const getMonitorFilters = async (
|
||||
data: {
|
||||
context: RouteContext;
|
||||
} & Filters
|
||||
) => {
|
||||
const { context, locations } = data;
|
||||
export const getMonitorFilters = async (context: RouteContext) => {
|
||||
const {
|
||||
tags,
|
||||
monitorTypes,
|
||||
locations,
|
||||
filter = '',
|
||||
projects,
|
||||
schedules,
|
||||
monitorQueryIds,
|
||||
} = context.request.query;
|
||||
const locationFilter = await parseLocationFilter(context, locations);
|
||||
|
||||
return parseArrayFilters({
|
||||
...data,
|
||||
filter,
|
||||
tags,
|
||||
monitorTypes,
|
||||
locations,
|
||||
projects,
|
||||
schedules,
|
||||
monitorQueryIds,
|
||||
locationFilter,
|
||||
});
|
||||
};
|
||||
|
@ -260,7 +253,7 @@ export const isMonitorsQueryFiltered = (monitorQuery: MonitorsQuery) => {
|
|||
);
|
||||
};
|
||||
|
||||
function parseMappingKey(key: string | undefined) {
|
||||
export function parseMappingKey(key: string | undefined) {
|
||||
switch (key) {
|
||||
case 'schedule.keyword':
|
||||
return 'schedule.number';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SyntheticsRestApiRouteFactory } from '../types';
|
||||
import { syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
import { monitorAttributes, syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
import { ConfigKey, MonitorFiltersResult } from '../../../common/runtime_types';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
|
||||
|
@ -87,31 +87,31 @@ export const getSyntheticsFilters: SyntheticsRestApiRouteFactory<MonitorFiltersR
|
|||
const aggs = {
|
||||
monitorTypes: {
|
||||
terms: {
|
||||
field: `${syntheticsMonitorType}.attributes.${ConfigKey.MONITOR_TYPE}.keyword`,
|
||||
field: `${monitorAttributes}.${ConfigKey.MONITOR_TYPE}.keyword`,
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
terms: {
|
||||
field: `${syntheticsMonitorType}.attributes.${ConfigKey.TAGS}`,
|
||||
field: `${monitorAttributes}.${ConfigKey.TAGS}`,
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
locations: {
|
||||
terms: {
|
||||
field: `${syntheticsMonitorType}.attributes.${ConfigKey.LOCATIONS}.id`,
|
||||
field: `${monitorAttributes}.${ConfigKey.LOCATIONS}.id`,
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
projects: {
|
||||
terms: {
|
||||
field: `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}`,
|
||||
field: `${monitorAttributes}.${ConfigKey.PROJECT_ID}`,
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
schedules: {
|
||||
terms: {
|
||||
field: `${syntheticsMonitorType}.attributes.${ConfigKey.SCHEDULE}.number`,
|
||||
field: `${monitorAttributes}.${ConfigKey.SCHEDULE}.number`,
|
||||
size: 10000,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -7,14 +7,13 @@
|
|||
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { SavedObject } from '@kbn/core-saved-objects-common/src/server_types';
|
||||
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { isValidNamespace } from '@kbn/fleet-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DeleteMonitorAPI } from '../services/delete_monitor_api';
|
||||
import { parseMonitorLocations } from './utils';
|
||||
import { MonitorValidationError } from '../monitor_validation';
|
||||
import { getSavedObjectKqlFilter } from '../../common';
|
||||
import { monitorAttributes, syntheticsMonitorType } from '../../../../common/types/saved_objects';
|
||||
import { monitorAttributes } from '../../../../common/types/saved_objects';
|
||||
import { PrivateLocationAttributes } from '../../../runtime_types/private_locations';
|
||||
import { ConfigKey } from '../../../../common/constants/monitor_management';
|
||||
import {
|
||||
|
@ -37,7 +36,6 @@ import { triggerTestNow } from '../../synthetics_service/test_now_monitor';
|
|||
import { DefaultAlertService } from '../../default_alerts/default_alert_service';
|
||||
import { RouteContext } from '../../types';
|
||||
import { formatTelemetryEvent, sendTelemetryEvents } from '../../telemetry/monitor_upgrade_sender';
|
||||
import { formatSecrets } from '../../../synthetics_service/utils';
|
||||
import { formatKibanaNamespace } from '../../../../common/formatters';
|
||||
import { getPrivateLocations } from '../../../synthetics_service/get_private_locations';
|
||||
|
||||
|
@ -63,7 +61,7 @@ export class AddEditMonitorAPI {
|
|||
id?: string;
|
||||
normalizedMonitor: SyntheticsMonitor;
|
||||
}) {
|
||||
const { savedObjectsClient, server, syntheticsMonitorClient, spaceId } = this.routeContext;
|
||||
const { server, syntheticsMonitorClient, spaceId } = this.routeContext;
|
||||
const newMonitorId = id ?? uuidV4();
|
||||
|
||||
let monitorSavedObject: SavedObject<EncryptedSyntheticsMonitorAttributes> | null = null;
|
||||
|
@ -73,10 +71,9 @@ export class AddEditMonitorAPI {
|
|||
});
|
||||
|
||||
try {
|
||||
const newMonitorPromise = this.createNewSavedObjectMonitor({
|
||||
const newMonitorPromise = this.routeContext.monitorConfigRepository.create({
|
||||
normalizedMonitor: monitorWithNamespace,
|
||||
id: newMonitorId,
|
||||
savedObjectsClient,
|
||||
});
|
||||
|
||||
const syncErrorsPromise = syntheticsMonitorClient.addMonitors(
|
||||
|
@ -132,32 +129,6 @@ export class AddEditMonitorAPI {
|
|||
}
|
||||
}
|
||||
|
||||
async createNewSavedObjectMonitor({
|
||||
id,
|
||||
savedObjectsClient,
|
||||
normalizedMonitor,
|
||||
}: {
|
||||
id: string;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
normalizedMonitor: SyntheticsMonitor;
|
||||
}) {
|
||||
return await savedObjectsClient.create<EncryptedSyntheticsMonitorAttributes>(
|
||||
syntheticsMonitorType,
|
||||
formatSecrets({
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || id,
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
}),
|
||||
id
|
||||
? {
|
||||
id,
|
||||
overwrite: true,
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
validateMonitorType(monitorFields: MonitorFields, previousMonitor?: MonitorFields) {
|
||||
const { [ConfigKey.MONITOR_TYPE]: monitorType } = monitorFields;
|
||||
if (previousMonitor) {
|
||||
|
@ -237,11 +208,10 @@ export class AddEditMonitorAPI {
|
|||
}
|
||||
|
||||
async validateUniqueMonitorName(name: string, id?: string) {
|
||||
const { savedObjectsClient } = this.routeContext;
|
||||
const { monitorConfigRepository } = this.routeContext;
|
||||
const kqlFilter = getSavedObjectKqlFilter({ field: 'name.keyword', values: name });
|
||||
const { total } = await savedObjectsClient.find({
|
||||
const { total } = await monitorConfigRepository.find({
|
||||
perPage: 0,
|
||||
type: syntheticsMonitorType,
|
||||
filter: id ? `${kqlFilter} and not (${monitorAttributes}.config_id: ${id})` : kqlFilter,
|
||||
});
|
||||
|
||||
|
@ -330,14 +300,11 @@ export class AddEditMonitorAPI {
|
|||
}
|
||||
|
||||
async revertMonitorIfCreated({ newMonitorId }: { newMonitorId: string }) {
|
||||
const { server, savedObjectsClient } = this.routeContext;
|
||||
const { server, monitorConfigRepository } = this.routeContext;
|
||||
try {
|
||||
const encryptedMonitor = await savedObjectsClient.get<EncryptedSyntheticsMonitorAttributes>(
|
||||
syntheticsMonitorType,
|
||||
newMonitorId
|
||||
);
|
||||
const encryptedMonitor = await monitorConfigRepository.get(newMonitorId);
|
||||
if (encryptedMonitor) {
|
||||
await savedObjectsClient.delete(syntheticsMonitorType, newMonitorId);
|
||||
await monitorConfigRepository.delete(newMonitorId);
|
||||
|
||||
const deleteMonitorAPI = new DeleteMonitorAPI(this.routeContext);
|
||||
await deleteMonitorAPI.execute({
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { SavedObjectsClientContract, SavedObject } from '@kbn/core/server';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import pMap from 'p-map';
|
||||
import { SavedObjectsBulkResponse } from '@kbn/core-saved-objects-api-server';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
@ -13,8 +13,6 @@ import { SavedObjectError } from '@kbn/core-saved-objects-common';
|
|||
import { SyntheticsServerSetup } from '../../../types';
|
||||
import { RouteContext } from '../../types';
|
||||
import { formatTelemetryEvent, sendTelemetryEvents } from '../../telemetry/monitor_upgrade_sender';
|
||||
import { formatSecrets } from '../../../synthetics_service/utils';
|
||||
import { syntheticsMonitorType } from '../../../../common/types/saved_objects';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitorAttributes,
|
||||
|
@ -25,28 +23,6 @@ import {
|
|||
} from '../../../../common/runtime_types';
|
||||
import { DeleteMonitorAPI } from '../services/delete_monitor_api';
|
||||
|
||||
export const createNewSavedObjectMonitorBulk = async ({
|
||||
soClient,
|
||||
monitorsToCreate,
|
||||
}: {
|
||||
soClient: SavedObjectsClientContract;
|
||||
monitorsToCreate: Array<{ id: string; monitor: MonitorFields }>;
|
||||
}) => {
|
||||
const newMonitors = monitorsToCreate.map(({ id, monitor }) => ({
|
||||
id,
|
||||
type: syntheticsMonitorType,
|
||||
attributes: formatSecrets({
|
||||
...monitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: monitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || id,
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = await soClient.bulkCreate<EncryptedSyntheticsMonitorAttributes>(newMonitors);
|
||||
return result.saved_objects;
|
||||
};
|
||||
|
||||
type MonitorSavedObject = SavedObject<EncryptedSyntheticsMonitorAttributes>;
|
||||
|
||||
type CreatedMonitors =
|
||||
|
@ -63,7 +39,7 @@ export const syncNewMonitorBulk = async ({
|
|||
privateLocations: SyntheticsPrivateLocations;
|
||||
spaceId: string;
|
||||
}) => {
|
||||
const { server, savedObjectsClient, syntheticsMonitorClient } = routeContext;
|
||||
const { server, syntheticsMonitorClient, monitorConfigRepository } = routeContext;
|
||||
let newMonitors: CreatedMonitors | null = null;
|
||||
|
||||
const monitorsToCreate = normalizedMonitors.map((monitor) => {
|
||||
|
@ -81,9 +57,8 @@ export const syncNewMonitorBulk = async ({
|
|||
|
||||
try {
|
||||
const [createdMonitors, [policiesResult, syncErrors]] = await Promise.all([
|
||||
createNewSavedObjectMonitorBulk({
|
||||
monitorsToCreate,
|
||||
soClient: savedObjectsClient,
|
||||
monitorConfigRepository.createBulk({
|
||||
monitors: monitorsToCreate,
|
||||
}),
|
||||
syntheticsMonitorClient.addMonitors(monitorsToCreate, privateLocations, spaceId),
|
||||
]);
|
||||
|
@ -182,12 +157,9 @@ export const deleteMonitorIfCreated = async ({
|
|||
routeContext: RouteContext;
|
||||
newMonitorId: string;
|
||||
}) => {
|
||||
const { server, savedObjectsClient } = routeContext;
|
||||
const { server, monitorConfigRepository } = routeContext;
|
||||
try {
|
||||
const encryptedMonitor = await savedObjectsClient.get<EncryptedSyntheticsMonitorAttributes>(
|
||||
syntheticsMonitorType,
|
||||
newMonitorId
|
||||
);
|
||||
const encryptedMonitor = await monitorConfigRepository.get(newMonitorId);
|
||||
if (encryptedMonitor) {
|
||||
const deleteMonitorAPI = new DeleteMonitorAPI(routeContext);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { SavedObject, SavedObjectsUpdateResponse } from '@kbn/core/server';
|
||||
import { SavedObjectError } from '@kbn/core-saved-objects-common';
|
||||
import { RouteContext } from '../../types';
|
||||
import { syntheticsMonitorType } from '../../../../common/types/saved_objects';
|
||||
import { FailedPolicyUpdate } from '../../../synthetics_service/private_location/synthetics_private_location';
|
||||
import {
|
||||
ConfigKey,
|
||||
|
@ -31,27 +30,6 @@ export interface MonitorConfigUpdate {
|
|||
decryptedPreviousMonitor: SavedObject<SyntheticsMonitorWithSecretsAttributes>;
|
||||
}
|
||||
|
||||
const updateConfigSavedObjects = async ({
|
||||
routeContext,
|
||||
monitorsToUpdate,
|
||||
}: {
|
||||
routeContext: RouteContext;
|
||||
monitorsToUpdate: MonitorConfigUpdate[];
|
||||
}) => {
|
||||
return await routeContext.savedObjectsClient.bulkUpdate<MonitorFields>(
|
||||
monitorsToUpdate.map(({ monitorWithRevision, decryptedPreviousMonitor }) => ({
|
||||
type: syntheticsMonitorType,
|
||||
id: decryptedPreviousMonitor.id,
|
||||
attributes: {
|
||||
...monitorWithRevision,
|
||||
[ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id,
|
||||
[ConfigKey.MONITOR_QUERY_ID]:
|
||||
monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id,
|
||||
},
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
async function syncUpdatedMonitors({
|
||||
spaceId,
|
||||
privateLocations,
|
||||
|
@ -92,11 +70,20 @@ export const syncEditedMonitorBulk = async ({
|
|||
privateLocations: SyntheticsPrivateLocations;
|
||||
spaceId: string;
|
||||
}) => {
|
||||
const { server } = routeContext;
|
||||
const { server, monitorConfigRepository } = routeContext;
|
||||
|
||||
try {
|
||||
const data = monitorsToUpdate.map(({ monitorWithRevision, decryptedPreviousMonitor }) => ({
|
||||
id: decryptedPreviousMonitor.id,
|
||||
attributes: {
|
||||
...monitorWithRevision,
|
||||
[ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id,
|
||||
[ConfigKey.MONITOR_QUERY_ID]:
|
||||
monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id,
|
||||
} as unknown as MonitorFields,
|
||||
}));
|
||||
const [editedMonitorSavedObjects, editSyncResponse] = await Promise.all([
|
||||
updateConfigSavedObjects({ monitorsToUpdate, routeContext }),
|
||||
monitorConfigRepository.bulkUpdate({ monitors: data }),
|
||||
syncUpdatedMonitors({ monitorsToUpdate, routeContext, spaceId, privateLocations }),
|
||||
]);
|
||||
|
||||
|
@ -145,15 +132,14 @@ export const rollbackCompletely = async ({
|
|||
monitorsToUpdate: MonitorConfigUpdate[];
|
||||
routeContext: RouteContext;
|
||||
}) => {
|
||||
const { savedObjectsClient, server } = routeContext;
|
||||
const { server, monitorConfigRepository } = routeContext;
|
||||
try {
|
||||
await savedObjectsClient.bulkUpdate<MonitorFields>(
|
||||
monitorsToUpdate.map(({ decryptedPreviousMonitor }) => ({
|
||||
type: syntheticsMonitorType,
|
||||
await monitorConfigRepository.bulkUpdate({
|
||||
monitors: monitorsToUpdate.map(({ decryptedPreviousMonitor }) => ({
|
||||
id: decryptedPreviousMonitor.id,
|
||||
attributes: decryptedPreviousMonitor.attributes,
|
||||
}))
|
||||
);
|
||||
attributes: decryptedPreviousMonitor.attributes as unknown as MonitorFields,
|
||||
})),
|
||||
});
|
||||
} catch (e) {
|
||||
server.logger.error(`Unable to rollback Synthetics monitors edit ${e.message} `);
|
||||
}
|
||||
|
@ -173,7 +159,7 @@ export const rollbackFailedUpdates = async ({
|
|||
if (!failedPolicyUpdates || failedPolicyUpdates.length === 0) {
|
||||
return;
|
||||
}
|
||||
const { server, savedObjectsClient } = routeContext;
|
||||
const { server, monitorConfigRepository } = routeContext;
|
||||
|
||||
try {
|
||||
const failedConfigs: Record<
|
||||
|
@ -195,13 +181,12 @@ export const rollbackFailedUpdates = async ({
|
|||
return failedConfigs[decryptedPreviousMonitor.id];
|
||||
})
|
||||
.map(({ decryptedPreviousMonitor }) => ({
|
||||
type: syntheticsMonitorType,
|
||||
id: decryptedPreviousMonitor.id,
|
||||
attributes: decryptedPreviousMonitor.attributes,
|
||||
attributes: decryptedPreviousMonitor.attributes as unknown as MonitorFields,
|
||||
}));
|
||||
|
||||
if (monitorsToRevert.length > 0) {
|
||||
await savedObjectsClient.bulkUpdate<MonitorFields>(monitorsToRevert);
|
||||
await monitorConfigRepository.bulkUpdate({ monitors: monitorsToRevert });
|
||||
}
|
||||
return failedConfigs;
|
||||
} catch (e) {
|
||||
|
|
|
@ -13,7 +13,6 @@ import { ConfigKey, EncryptedSyntheticsMonitorAttributes } from '../../../common
|
|||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
import { getMonitorNotFoundResponse } from '../synthetics_service/service_errors';
|
||||
import { mapSavedObjectToMonitor } from './formatters/saved_object_to_monitor';
|
||||
import { getSyntheticsMonitor } from '../../queries/get_monitor';
|
||||
|
||||
export const getSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
method: 'GET',
|
||||
|
@ -39,6 +38,7 @@ export const getSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
|||
server: { encryptedSavedObjects, coreStart },
|
||||
savedObjectsClient,
|
||||
spaceId,
|
||||
monitorConfigRepository,
|
||||
}): Promise<any> => {
|
||||
const { monitorId } = request.params;
|
||||
try {
|
||||
|
@ -53,13 +53,7 @@ export const getSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
|||
|
||||
if (Boolean(canSave)) {
|
||||
// only user with write permissions can decrypt the monitor
|
||||
const encryptedSavedObjectsClient = encryptedSavedObjects.getClient();
|
||||
|
||||
const monitor = await getSyntheticsMonitor({
|
||||
monitorId,
|
||||
encryptedSavedObjectsClient,
|
||||
spaceId,
|
||||
});
|
||||
const monitor = await monitorConfigRepository.getDecrypted(monitorId, spaceId);
|
||||
return { ...mapSavedObjectToMonitor({ monitor, internal }), spaceId };
|
||||
} else {
|
||||
return {
|
||||
|
|
|
@ -8,7 +8,6 @@ import { mapSavedObjectToMonitor } from './formatters/saved_object_to_monitor';
|
|||
import { SyntheticsRestApiRouteFactory } from '../types';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
import { getMonitors, isMonitorsQueryFiltered, QuerySchema } from '../common';
|
||||
import { syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
|
||||
export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
method: 'GET',
|
||||
|
@ -20,11 +19,10 @@ export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () =>
|
|||
},
|
||||
},
|
||||
handler: async (routeContext): Promise<any> => {
|
||||
const { request, savedObjectsClient, syntheticsMonitorClient } = routeContext;
|
||||
const { request, syntheticsMonitorClient, monitorConfigRepository } = routeContext;
|
||||
const totalCountQuery = async () => {
|
||||
if (isMonitorsQueryFiltered(request.query)) {
|
||||
return savedObjectsClient.find({
|
||||
type: syntheticsMonitorType,
|
||||
return monitorConfigRepository.find({
|
||||
perPage: 0,
|
||||
page: 1,
|
||||
});
|
||||
|
|
|
@ -65,13 +65,10 @@ export const addSyntheticsProjectMonitorRoute: SyntheticsRestApiRouteFactory = (
|
|||
return response.forbidden({ body: { message: permissionError } });
|
||||
}
|
||||
|
||||
const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
|
||||
|
||||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
routeContext,
|
||||
projectId: decodedProjectName,
|
||||
spaceId,
|
||||
encryptedSavedObjectsClient,
|
||||
monitors,
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { DeleteMonitorAPI } from '../services/delete_monitor_api';
|
||||
import { SyntheticsRestApiRouteFactory } from '../../types';
|
||||
import { syntheticsMonitorType } from '../../../../common/types/saved_objects';
|
||||
import { monitorAttributes } from '../../../../common/types/saved_objects';
|
||||
import { ConfigKey } from '../../../../common/runtime_types';
|
||||
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
|
||||
import { getMonitors, getSavedObjectKqlFilter } from '../../common';
|
||||
|
@ -40,7 +40,7 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory
|
|||
|
||||
await validateSpaceId(routeContext);
|
||||
|
||||
const deleteFilter = `${syntheticsMonitorType}.attributes.${
|
||||
const deleteFilter = `${monitorAttributes}.${
|
||||
ConfigKey.PROJECT_ID
|
||||
}: "${decodedProjectName}" AND ${getSavedObjectKqlFilter({
|
||||
field: 'journey_id',
|
||||
|
|
|
@ -130,7 +130,7 @@ export class DeleteMonitorAPI {
|
|||
}: {
|
||||
monitors: Array<SavedObject<SyntheticsMonitor | EncryptedSyntheticsMonitorAttributes>>;
|
||||
}) {
|
||||
const { savedObjectsClient, server, spaceId, syntheticsMonitorClient } = this.routeContext;
|
||||
const { server, spaceId, syntheticsMonitorClient } = this.routeContext;
|
||||
const { logger, telemetry, stackVersion } = server;
|
||||
|
||||
try {
|
||||
|
@ -139,15 +139,14 @@ export class DeleteMonitorAPI {
|
|||
...normalizedMonitor.attributes,
|
||||
id: normalizedMonitor.attributes[ConfigKey.MONITOR_QUERY_ID],
|
||||
})) as SyntheticsMonitorWithId[],
|
||||
savedObjectsClient,
|
||||
spaceId
|
||||
);
|
||||
|
||||
const deletePromises = savedObjectsClient.bulkDelete(
|
||||
monitors.map((monitor) => ({ type: syntheticsMonitorType, id: monitor.id }))
|
||||
const deletePromise = this.routeContext.monitorConfigRepository.bulkDelete(
|
||||
monitors.map((monitor) => monitor.id)
|
||||
);
|
||||
|
||||
const [errors, result] = await Promise.all([deleteSyncPromise, deletePromises]);
|
||||
const [errors, result] = await Promise.all([deleteSyncPromise, deletePromise]);
|
||||
|
||||
monitors.forEach((monitor) => {
|
||||
sendTelemetryEvents(
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server';
|
||||
import * as monitorsFns from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { EncryptedSyntheticsMonitorAttributes } from '../../../common/runtime_types';
|
||||
import { getUptimeESMockClient } from '../../queries/test_helpers';
|
||||
|
||||
|
@ -158,7 +157,7 @@ describe('current status route', () => {
|
|||
})
|
||||
);
|
||||
const routeContext: any = {
|
||||
request: {},
|
||||
request: { query: {} },
|
||||
syntheticsEsClient,
|
||||
};
|
||||
|
||||
|
@ -316,7 +315,7 @@ describe('current status route', () => {
|
|||
);
|
||||
|
||||
const routeContext: any = {
|
||||
request: {},
|
||||
request: { query: {} },
|
||||
syntheticsEsClient,
|
||||
};
|
||||
|
||||
|
@ -420,7 +419,7 @@ describe('current status route', () => {
|
|||
})
|
||||
);
|
||||
const routeContext: any = {
|
||||
request: {},
|
||||
request: { query: {} },
|
||||
syntheticsEsClient,
|
||||
};
|
||||
|
||||
|
@ -536,7 +535,7 @@ describe('current status route', () => {
|
|||
[['North America - US Central', 'US Central QA'], 2],
|
||||
[undefined, 2],
|
||||
])('handles disabled count when using location filters', async (locations, disabledCount) => {
|
||||
jest.spyOn(monitorsFns, 'getAllMonitors').mockResolvedValue([
|
||||
const getAll = jest.fn().mockResolvedValue([
|
||||
{
|
||||
type: 'synthetics-monitor',
|
||||
id: 'a9a94f2f-47ba-4fe2-afaa-e5cd29b281f1',
|
||||
|
@ -691,6 +690,9 @@ describe('current status route', () => {
|
|||
},
|
||||
},
|
||||
syntheticsEsClient,
|
||||
monitorConfigRepository: {
|
||||
getAll,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await overviewStatusService.getOverviewStatus();
|
||||
|
@ -708,7 +710,7 @@ describe('current status route', () => {
|
|||
[['North America - US Central', 'US Central QA'], 2],
|
||||
[undefined, 2],
|
||||
])('handles pending count when using location filters', async (locations, pending) => {
|
||||
jest.spyOn(monitorsFns, 'getAllMonitors').mockResolvedValue([
|
||||
const getAll = jest.fn().mockResolvedValue([
|
||||
{
|
||||
type: 'synthetics-monitor',
|
||||
id: 'a9a94f2f-47ba-4fe2-afaa-e5cd29b281f1',
|
||||
|
@ -761,6 +763,9 @@ describe('current status route', () => {
|
|||
},
|
||||
},
|
||||
syntheticsEsClient,
|
||||
monitorConfigRepository: {
|
||||
getAll,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await overviewStatusService.getOverviewStatus();
|
||||
|
|
|
@ -12,10 +12,7 @@ import { isEmpty } from 'lodash';
|
|||
import { withApmSpan } from '@kbn/apm-data-access-plugin/server/utils/with_apm_span';
|
||||
import { asMutableArray } from '../../../common/utils/as_mutable_array';
|
||||
import { getMonitorFilters, OverviewStatusQuery } from '../common';
|
||||
import {
|
||||
getAllMonitors,
|
||||
processMonitors,
|
||||
} from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { processMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors';
|
||||
import { ConfigKey } from '../../../common/constants/monitor_management';
|
||||
import { RouteContext } from '../types';
|
||||
import {
|
||||
|
@ -47,13 +44,7 @@ export class OverviewStatusService {
|
|||
) {}
|
||||
|
||||
async getOverviewStatus() {
|
||||
const { request } = this.routeContext;
|
||||
const queryParams = request.query as OverviewStatusQuery;
|
||||
|
||||
this.filterData = await getMonitorFilters({
|
||||
...queryParams,
|
||||
context: this.routeContext,
|
||||
});
|
||||
this.filterData = await getMonitorFilters(this.routeContext);
|
||||
|
||||
const [allConfigs, statusResult] = await Promise.all([
|
||||
this.getMonitorConfigs(),
|
||||
|
@ -312,7 +303,7 @@ export class OverviewStatusService {
|
|||
}
|
||||
|
||||
async getMonitorConfigs() {
|
||||
const { savedObjectsClient, request } = this.routeContext;
|
||||
const { request } = this.routeContext;
|
||||
const { query, showFromAllSpaces } = request.query || {};
|
||||
/**
|
||||
* Walk through all monitor saved objects, bucket IDs by disabled/enabled status.
|
||||
|
@ -323,8 +314,7 @@ export class OverviewStatusService {
|
|||
|
||||
const { filtersStr } = this.filterData;
|
||||
|
||||
return await getAllMonitors({
|
||||
soClient: savedObjectsClient,
|
||||
return await this.routeContext.monitorConfigRepository.getAll({
|
||||
showFromAllSpaces,
|
||||
search: query ? `${query}*` : '',
|
||||
filter: filtersStr,
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
|
||||
import { SyntheticsRestApiRouteFactory } from '../types';
|
||||
import { getPrivateLocations } from '../../synthetics_service/get_private_locations';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
|
@ -17,16 +16,15 @@ export const syncParamsSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = ()
|
|||
handler: async ({
|
||||
savedObjectsClient,
|
||||
syntheticsMonitorClient,
|
||||
request,
|
||||
spaceId,
|
||||
server,
|
||||
}): Promise<any> => {
|
||||
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
|
||||
|
||||
const allPrivateLocations = await getPrivateLocations(savedObjectsClient);
|
||||
|
||||
await syntheticsMonitorClient.syncGlobalParams({
|
||||
spaceId,
|
||||
allPrivateLocations,
|
||||
soClient: savedObjectsClient,
|
||||
encryptedSavedObjects: server.encryptedSavedObjects,
|
||||
});
|
||||
|
||||
|
|
|
@ -62,22 +62,15 @@ export const getSyntheticsSuggestionsRoute: SyntheticsRestApiRouteFactory<
|
|||
},
|
||||
handler: async (route): Promise<any> => {
|
||||
const {
|
||||
savedObjectsClient,
|
||||
server: { logger },
|
||||
monitorConfigRepository,
|
||||
} = route;
|
||||
const { tags, locations, projects, monitorQueryIds, query } = route.request.query;
|
||||
const { query } = route.request.query;
|
||||
|
||||
const { filtersStr } = await getMonitorFilters({
|
||||
tags,
|
||||
locations,
|
||||
projects,
|
||||
monitorQueryIds,
|
||||
context: route,
|
||||
});
|
||||
const { filtersStr } = await getMonitorFilters(route);
|
||||
const { allLocations = [] } = await getAllLocations(route);
|
||||
try {
|
||||
const data = await savedObjectsClient.find<EncryptedSyntheticsMonitorAttributes>({
|
||||
type: syntheticsMonitorType,
|
||||
const data = await monitorConfigRepository.find<EncryptedSyntheticsMonitorAttributes>({
|
||||
perPage: 0,
|
||||
filter: filtersStr ? `${filtersStr}` : undefined,
|
||||
aggs,
|
||||
|
|
|
@ -61,7 +61,6 @@ export const runOnceSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () =
|
|||
id: monitorId,
|
||||
testRunId: monitorId,
|
||||
},
|
||||
savedObjectsClient,
|
||||
privateLocations,
|
||||
spaceId,
|
||||
true
|
||||
|
|
|
@ -58,7 +58,6 @@ export const triggerTestNow = async (
|
|||
id: monitorId,
|
||||
testRunId,
|
||||
},
|
||||
savedObjectsClient,
|
||||
privateLocations,
|
||||
spaceId
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
HttpResponsePayload,
|
||||
ResponseError,
|
||||
} from '@kbn/core-http-server';
|
||||
import { MonitorConfigRepository } from '../services/monitor_config_repository';
|
||||
import { SyntheticsEsClient } from '../lib';
|
||||
import { SyntheticsServerSetup, UptimeRequestHandlerContext } from '../types';
|
||||
import { SyntheticsMonitorClient } from '../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
|
@ -84,16 +85,6 @@ export type SyntheticsRouteWrapper = (
|
|||
syntheticsMonitorClient: SyntheticsMonitorClient
|
||||
) => UMKibanaRoute;
|
||||
|
||||
export interface UptimeRouteContext {
|
||||
syntheticsEsClient: SyntheticsEsClient;
|
||||
context: UptimeRequestHandlerContext;
|
||||
request: SyntheticsRequest;
|
||||
response: KibanaResponseFactory;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
server: SyntheticsServerSetup;
|
||||
subject?: Subject<unknown>;
|
||||
}
|
||||
|
||||
export interface RouteContext<
|
||||
Params = Record<string, any>,
|
||||
Query = Record<string, any>,
|
||||
|
@ -108,6 +99,7 @@ export interface RouteContext<
|
|||
syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
subject?: Subject<unknown>;
|
||||
spaceId: string;
|
||||
monitorConfigRepository: MonitorConfigRepository;
|
||||
}
|
||||
|
||||
export type SyntheticsRouteHandler<
|
||||
|
|
|
@ -5,60 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindOptions,
|
||||
SavedObjectsFindResult,
|
||||
} from '@kbn/core-saved-objects-api-server';
|
||||
import { SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server';
|
||||
import { intersection } from 'lodash';
|
||||
import { withApmSpan } from '@kbn/apm-data-access-plugin/server/utils';
|
||||
import { periodToMs } from '../../routes/overview_status/utils';
|
||||
import { syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitorAttributes,
|
||||
SourceType,
|
||||
} from '../../../common/runtime_types';
|
||||
|
||||
export const getAllMonitors = async ({
|
||||
soClient,
|
||||
search,
|
||||
fields,
|
||||
filter,
|
||||
sortField = 'name.keyword',
|
||||
sortOrder = 'asc',
|
||||
searchFields,
|
||||
showFromAllSpaces,
|
||||
}: {
|
||||
soClient: SavedObjectsClientContract;
|
||||
search?: string;
|
||||
filter?: string;
|
||||
showFromAllSpaces?: boolean;
|
||||
} & Pick<SavedObjectsFindOptions, 'sortField' | 'sortOrder' | 'fields' | 'searchFields'>) => {
|
||||
return withApmSpan('get_all_monitors', async () => {
|
||||
const finder = soClient.createPointInTimeFinder<EncryptedSyntheticsMonitorAttributes>({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
search,
|
||||
sortField,
|
||||
sortOrder,
|
||||
fields,
|
||||
filter,
|
||||
searchFields,
|
||||
...(showFromAllSpaces && { namespaces: ['*'] }),
|
||||
});
|
||||
|
||||
const hits: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>> = [];
|
||||
for await (const result of finder.find()) {
|
||||
hits.push(...result.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return hits;
|
||||
});
|
||||
};
|
||||
|
||||
export const processMonitors = (
|
||||
allMonitors: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>>,
|
||||
queryLocations?: string[] | string
|
||||
|
|
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
* 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 { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
|
||||
import { MonitorConfigRepository } from './monitor_config_repository';
|
||||
import { syntheticsMonitorType } from '../../common/types/saved_objects';
|
||||
import { ConfigKey, SyntheticsMonitor } from '../../common/runtime_types';
|
||||
import * as utils from '../synthetics_service/utils';
|
||||
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
|
||||
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
|
||||
// Mock the utils functions
|
||||
jest.mock('../synthetics_service/utils', () => ({
|
||||
formatSecrets: jest.fn((data) => ({ ...data, formattedSecrets: true })),
|
||||
normalizeSecrets: jest.fn((data) => ({ ...data, normalizedSecrets: true })),
|
||||
}));
|
||||
|
||||
// Mock the AMP span
|
||||
jest.mock('@kbn/apm-data-access-plugin/server/utils/with_apm_span', () => ({
|
||||
withApmSpan: jest.fn((spanName, fn) => fn()),
|
||||
}));
|
||||
|
||||
describe('MonitorConfigRepository', () => {
|
||||
let soClient: jest.Mocked<SavedObjectsClientContract>;
|
||||
let encryptedSavedObjectsClient: jest.Mocked<EncryptedSavedObjectsClient>;
|
||||
let repository: MonitorConfigRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
soClient = savedObjectsClientMock.create();
|
||||
encryptedSavedObjectsClient = encryptedSavedObjectsMock
|
||||
.createStart()
|
||||
.getClient() as jest.Mocked<EncryptedSavedObjectsClient>;
|
||||
repository = new MonitorConfigRepository(soClient, encryptedSavedObjectsClient);
|
||||
|
||||
// Clear all mocks before each test
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it('should get a monitor by id', async () => {
|
||||
const id = 'test-id';
|
||||
const mockMonitor = {
|
||||
id,
|
||||
attributes: { name: 'Test Monitor' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
};
|
||||
|
||||
soClient.get.mockResolvedValue(mockMonitor);
|
||||
|
||||
const result = await repository.get(id);
|
||||
|
||||
expect(soClient.get).toHaveBeenCalledWith(syntheticsMonitorType, id);
|
||||
expect(result).toBe(mockMonitor);
|
||||
});
|
||||
|
||||
it('should propagate errors', async () => {
|
||||
const id = 'test-id';
|
||||
const error = new Error('Not found');
|
||||
|
||||
soClient.get.mockRejectedValue(error);
|
||||
|
||||
await expect(repository.get(id)).rejects.toThrow(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecrypted', () => {
|
||||
it('should get and decrypt a monitor by id and space', async () => {
|
||||
const id = 'test-id';
|
||||
const spaceId = 'test-space';
|
||||
const mockDecryptedMonitor = {
|
||||
id,
|
||||
attributes: { name: 'Test Monitor', secrets: 'decrypted' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
};
|
||||
|
||||
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(
|
||||
mockDecryptedMonitor
|
||||
);
|
||||
(utils.normalizeSecrets as jest.Mock).mockReturnValue({
|
||||
...mockDecryptedMonitor,
|
||||
normalizedSecrets: true,
|
||||
});
|
||||
|
||||
const result = await repository.getDecrypted(id, spaceId);
|
||||
|
||||
expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).toHaveBeenCalledWith(
|
||||
syntheticsMonitorType,
|
||||
id,
|
||||
{ namespace: spaceId }
|
||||
);
|
||||
expect(utils.normalizeSecrets).toHaveBeenCalledWith(mockDecryptedMonitor);
|
||||
expect(result).toEqual({ ...mockDecryptedMonitor, normalizedSecrets: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a monitor with an id', async () => {
|
||||
const id = 'test-id';
|
||||
const normalizedMonitor = {
|
||||
name: 'Test Monitor',
|
||||
[ConfigKey.CUSTOM_HEARTBEAT_ID]: 'custom-id',
|
||||
} as unknown as SyntheticsMonitor;
|
||||
|
||||
const mockCreatedMonitor = {
|
||||
id,
|
||||
attributes: { name: 'Test Monitor' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
};
|
||||
soClient.create.mockResolvedValue(mockCreatedMonitor);
|
||||
|
||||
const result = await repository.create({
|
||||
id,
|
||||
normalizedMonitor,
|
||||
});
|
||||
|
||||
expect(utils.formatSecrets).toHaveBeenCalledWith({
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: 'custom-id',
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
});
|
||||
|
||||
expect(soClient.create).toHaveBeenCalledWith(
|
||||
syntheticsMonitorType,
|
||||
{
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: 'custom-id',
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
formattedSecrets: true,
|
||||
},
|
||||
{ id, overwrite: true }
|
||||
);
|
||||
|
||||
expect(result).toBe(mockCreatedMonitor);
|
||||
});
|
||||
|
||||
it('should create a monitor without an id', async () => {
|
||||
const normalizedMonitor = {
|
||||
name: 'Test Monitor',
|
||||
} as unknown as SyntheticsMonitor;
|
||||
|
||||
const mockCreatedMonitor = {
|
||||
id: 'generated-id',
|
||||
attributes: { name: 'Test Monitor' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
};
|
||||
soClient.create.mockResolvedValue(mockCreatedMonitor);
|
||||
|
||||
const result = await repository.create({
|
||||
id: '',
|
||||
normalizedMonitor,
|
||||
});
|
||||
|
||||
expect(utils.formatSecrets).toHaveBeenCalledWith({
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: '',
|
||||
[ConfigKey.CONFIG_ID]: '',
|
||||
revision: 1,
|
||||
});
|
||||
|
||||
expect(soClient.create).toHaveBeenCalledWith(
|
||||
syntheticsMonitorType,
|
||||
{
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: '',
|
||||
[ConfigKey.CONFIG_ID]: '',
|
||||
revision: 1,
|
||||
formattedSecrets: true,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
expect(result).toBe(mockCreatedMonitor);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createBulk', () => {
|
||||
it('should create multiple monitors in bulk', async () => {
|
||||
const monitors = [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
monitor: {
|
||||
name: 'Test Monitor 1',
|
||||
[ConfigKey.CUSTOM_HEARTBEAT_ID]: 'custom-id-1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
monitor: {
|
||||
name: 'Test Monitor 2',
|
||||
},
|
||||
},
|
||||
] as any;
|
||||
|
||||
const mockBulkCreateResult = {
|
||||
saved_objects: [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Test Monitor 1' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
attributes: { name: 'Test Monitor 2' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
soClient.bulkCreate.mockResolvedValue(mockBulkCreateResult);
|
||||
|
||||
const result = await repository.createBulk({ monitors });
|
||||
|
||||
expect(soClient.bulkCreate).toHaveBeenCalledWith([
|
||||
{
|
||||
id: 'test-id-1',
|
||||
type: syntheticsMonitorType,
|
||||
attributes: {
|
||||
name: 'Test Monitor 1',
|
||||
[ConfigKey.CUSTOM_HEARTBEAT_ID]: 'custom-id-1',
|
||||
[ConfigKey.MONITOR_QUERY_ID]: 'custom-id-1',
|
||||
[ConfigKey.CONFIG_ID]: 'test-id-1',
|
||||
revision: 1,
|
||||
formattedSecrets: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
type: syntheticsMonitorType,
|
||||
attributes: {
|
||||
name: 'Test Monitor 2',
|
||||
[ConfigKey.MONITOR_QUERY_ID]: 'test-id-2',
|
||||
[ConfigKey.CONFIG_ID]: 'test-id-2',
|
||||
revision: 1,
|
||||
formattedSecrets: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result).toBe(mockBulkCreateResult.saved_objects);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulkUpdate', () => {
|
||||
it('should update multiple monitors in bulk', async () => {
|
||||
const monitors = [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: {
|
||||
name: 'Updated Monitor 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
attributes: {
|
||||
name: 'Updated Monitor 2',
|
||||
},
|
||||
},
|
||||
] as any;
|
||||
|
||||
const mockBulkUpdateResult = {
|
||||
saved_objects: [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Updated Monitor 1' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
attributes: { name: 'Updated Monitor 2' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
soClient.bulkUpdate.mockResolvedValue(mockBulkUpdateResult);
|
||||
|
||||
const result = await repository.bulkUpdate({ monitors });
|
||||
|
||||
expect(soClient.bulkUpdate).toHaveBeenCalledWith([
|
||||
{
|
||||
type: syntheticsMonitorType,
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Updated Monitor 1' },
|
||||
},
|
||||
{
|
||||
type: syntheticsMonitorType,
|
||||
id: 'test-id-2',
|
||||
attributes: { name: 'Updated Monitor 2' },
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result).toBe(mockBulkUpdateResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('find', () => {
|
||||
it('should find monitors with options', async () => {
|
||||
const options = {
|
||||
search: 'test',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
sortField: 'name',
|
||||
sortOrder: 'asc' as const,
|
||||
};
|
||||
|
||||
const mockFindResult = {
|
||||
saved_objects: [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Test Monitor 1' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
attributes: { name: 'Test Monitor 2' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
total: 2,
|
||||
per_page: 10,
|
||||
page: 1,
|
||||
} as any;
|
||||
|
||||
soClient.find.mockResolvedValue(mockFindResult);
|
||||
|
||||
const result = await repository.find(options);
|
||||
|
||||
expect(soClient.find).toHaveBeenCalledWith({
|
||||
type: syntheticsMonitorType,
|
||||
...options,
|
||||
});
|
||||
|
||||
expect(result).toBe(mockFindResult);
|
||||
});
|
||||
|
||||
it('should use default perPage if not provided', async () => {
|
||||
const options = {
|
||||
search: 'test',
|
||||
};
|
||||
|
||||
const mockFindResult = {
|
||||
saved_objects: [],
|
||||
total: 0,
|
||||
per_page: 5000,
|
||||
page: 1,
|
||||
};
|
||||
|
||||
soClient.find.mockResolvedValue(mockFindResult);
|
||||
|
||||
await repository.find(options);
|
||||
|
||||
expect(soClient.find).toHaveBeenCalledWith({
|
||||
type: syntheticsMonitorType,
|
||||
search: 'test',
|
||||
perPage: 5000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('findDecryptedMonitors', () => {
|
||||
it('should find decrypted monitors by space id and filter', async () => {
|
||||
const spaceId = 'test-space';
|
||||
const filter = 'attributes.name:test';
|
||||
|
||||
const mockDecryptedMonitors = [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Test Monitor 1', secrets: 'decrypted' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
id: 'test-id-2',
|
||||
attributes: { name: 'Test Monitor 2', secrets: 'decrypted' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockDecryptedMonitors };
|
||||
}),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
} as any;
|
||||
|
||||
encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser.mockReturnValue(
|
||||
pointInTimeFinderMock
|
||||
);
|
||||
|
||||
const result = await repository.findDecryptedMonitors({ spaceId, filter });
|
||||
|
||||
expect(
|
||||
encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser
|
||||
).toHaveBeenCalledWith({
|
||||
filter,
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 500,
|
||||
namespaces: [spaceId],
|
||||
});
|
||||
|
||||
expect(pointInTimeFinderMock.find).toHaveBeenCalled();
|
||||
expect(pointInTimeFinderMock.close).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockDecryptedMonitors);
|
||||
});
|
||||
|
||||
it('should handle finder.close errors', async () => {
|
||||
const spaceId = 'test-space';
|
||||
|
||||
const mockDecryptedMonitors = [
|
||||
{
|
||||
id: 'test-id-1',
|
||||
attributes: { name: 'Test Monitor 1', secrets: 'decrypted' },
|
||||
type: syntheticsMonitorType,
|
||||
references: [],
|
||||
},
|
||||
];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockDecryptedMonitors };
|
||||
}),
|
||||
close: jest.fn().mockRejectedValue(new Error('Close failed')),
|
||||
} as any;
|
||||
|
||||
encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser.mockReturnValue(
|
||||
pointInTimeFinderMock
|
||||
);
|
||||
|
||||
const result = await repository.findDecryptedMonitors({ spaceId });
|
||||
|
||||
expect(pointInTimeFinderMock.close).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockDecryptedMonitors);
|
||||
// Should not throw an error when close fails
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete a monitor by id', async () => {
|
||||
const id = 'test-id';
|
||||
const mockDeleteResult = { success: true };
|
||||
|
||||
soClient.delete.mockResolvedValue(mockDeleteResult);
|
||||
|
||||
const result = await repository.delete(id);
|
||||
|
||||
expect(soClient.delete).toHaveBeenCalledWith(syntheticsMonitorType, id);
|
||||
expect(result).toBe(mockDeleteResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulkDelete', () => {
|
||||
it('should delete multiple monitors by ids', async () => {
|
||||
const ids = ['test-id-1', 'test-id-2'];
|
||||
const mockBulkDeleteResult = { success: true } as any;
|
||||
|
||||
soClient.bulkDelete.mockResolvedValue(mockBulkDeleteResult);
|
||||
|
||||
const result = await repository.bulkDelete(ids);
|
||||
|
||||
expect(soClient.bulkDelete).toHaveBeenCalledWith([
|
||||
{ type: syntheticsMonitorType, id: 'test-id-1' },
|
||||
{ type: syntheticsMonitorType, id: 'test-id-2' },
|
||||
]);
|
||||
|
||||
expect(result).toBe(mockBulkDeleteResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAll', () => {
|
||||
it('should get all monitors with options', async () => {
|
||||
const options = {
|
||||
search: 'test',
|
||||
fields: ['name'],
|
||||
filter: 'attributes.enabled:true',
|
||||
sortField: 'name.keyword',
|
||||
sortOrder: 'asc' as const,
|
||||
searchFields: ['name'],
|
||||
showFromAllSpaces: true,
|
||||
};
|
||||
|
||||
const mockMonitors = [
|
||||
{ id: 'test-id-1', attributes: { name: 'Test Monitor 1' } },
|
||||
{ id: 'test-id-2', attributes: { name: 'Test Monitor 2' } },
|
||||
];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockMonitors };
|
||||
}),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
soClient.createPointInTimeFinder.mockReturnValue(pointInTimeFinderMock);
|
||||
|
||||
const result = await repository.getAll(options);
|
||||
|
||||
expect(soClient.createPointInTimeFinder).toHaveBeenCalledWith({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
search: 'test',
|
||||
fields: ['name'],
|
||||
filter: 'attributes.enabled:true',
|
||||
sortField: 'name.keyword',
|
||||
sortOrder: 'asc',
|
||||
searchFields: ['name'],
|
||||
namespaces: ['*'],
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockMonitors);
|
||||
});
|
||||
|
||||
it('should not include namespaces if showFromAllSpaces is false', async () => {
|
||||
const options = {
|
||||
search: 'test',
|
||||
showFromAllSpaces: false,
|
||||
};
|
||||
|
||||
const mockMonitors: any = [];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockMonitors };
|
||||
}),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
soClient.createPointInTimeFinder.mockReturnValue(pointInTimeFinderMock);
|
||||
|
||||
await repository.getAll(options);
|
||||
|
||||
expect(soClient.createPointInTimeFinder).toHaveBeenCalledWith({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
search: 'test',
|
||||
sortField: 'name.keyword',
|
||||
sortOrder: 'asc',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default sort options if not provided', async () => {
|
||||
const options = {
|
||||
search: 'test',
|
||||
};
|
||||
|
||||
const mockMonitors: any = [];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockMonitors };
|
||||
}),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
soClient.createPointInTimeFinder.mockReturnValue(pointInTimeFinderMock);
|
||||
|
||||
await repository.getAll(options);
|
||||
|
||||
expect(soClient.createPointInTimeFinder).toHaveBeenCalledWith({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
search: 'test',
|
||||
sortField: 'name.keyword',
|
||||
sortOrder: 'asc',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle finder.close errors', async () => {
|
||||
const options = { search: 'test' };
|
||||
|
||||
const mockMonitors = [{ id: 'test-id-1', attributes: { name: 'Test Monitor 1' } }];
|
||||
|
||||
const pointInTimeFinderMock = {
|
||||
find: jest.fn().mockImplementation(function* () {
|
||||
yield { saved_objects: mockMonitors };
|
||||
}),
|
||||
close: jest.fn().mockRejectedValue(new Error('Close failed')),
|
||||
};
|
||||
|
||||
soClient.createPointInTimeFinder.mockReturnValue(pointInTimeFinderMock);
|
||||
|
||||
const result = await repository.getAll(options);
|
||||
|
||||
expect(pointInTimeFinderMock.close).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockMonitors);
|
||||
// Should not throw an error when close fails
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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 {
|
||||
SavedObject,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindOptions,
|
||||
SavedObjectsFindResult,
|
||||
} from '@kbn/core-saved-objects-api-server';
|
||||
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { withApmSpan } from '@kbn/apm-data-access-plugin/server/utils/with_apm_span';
|
||||
import { formatSecrets, normalizeSecrets } from '../synthetics_service/utils';
|
||||
import { syntheticsMonitorType } from '../../common/types/saved_objects';
|
||||
import {
|
||||
ConfigKey,
|
||||
EncryptedSyntheticsMonitorAttributes,
|
||||
MonitorFields,
|
||||
SyntheticsMonitor,
|
||||
SyntheticsMonitorWithSecretsAttributes,
|
||||
} from '../../common/runtime_types';
|
||||
|
||||
export class MonitorConfigRepository {
|
||||
constructor(
|
||||
private soClient: SavedObjectsClientContract,
|
||||
private encryptedSavedObjectsClient: EncryptedSavedObjectsClient
|
||||
) {}
|
||||
|
||||
async get(id: string) {
|
||||
return await this.soClient.get<EncryptedSyntheticsMonitorAttributes>(syntheticsMonitorType, id);
|
||||
}
|
||||
|
||||
async getDecrypted(id: string, spaceId: string): Promise<SavedObject<SyntheticsMonitor>> {
|
||||
const decryptedMonitor =
|
||||
await this.encryptedSavedObjectsClient.getDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
|
||||
syntheticsMonitorType,
|
||||
id,
|
||||
{
|
||||
namespace: spaceId,
|
||||
}
|
||||
);
|
||||
return normalizeSecrets(decryptedMonitor);
|
||||
}
|
||||
|
||||
async create({ id, normalizedMonitor }: { id: string; normalizedMonitor: SyntheticsMonitor }) {
|
||||
return await this.soClient.create<EncryptedSyntheticsMonitorAttributes>(
|
||||
syntheticsMonitorType,
|
||||
formatSecrets({
|
||||
...normalizedMonitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || id,
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
}),
|
||||
id
|
||||
? {
|
||||
id,
|
||||
overwrite: true,
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
async createBulk({ monitors }: { monitors: Array<{ id: string; monitor: MonitorFields }> }) {
|
||||
const newMonitors = monitors.map(({ id, monitor }) => ({
|
||||
id,
|
||||
type: syntheticsMonitorType,
|
||||
attributes: formatSecrets({
|
||||
...monitor,
|
||||
[ConfigKey.MONITOR_QUERY_ID]: monitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || id,
|
||||
[ConfigKey.CONFIG_ID]: id,
|
||||
revision: 1,
|
||||
}),
|
||||
}));
|
||||
const result = await this.soClient.bulkCreate<EncryptedSyntheticsMonitorAttributes>(
|
||||
newMonitors
|
||||
);
|
||||
return result.saved_objects;
|
||||
}
|
||||
|
||||
async bulkUpdate({
|
||||
monitors,
|
||||
}: {
|
||||
monitors: Array<{
|
||||
attributes: MonitorFields;
|
||||
id: string;
|
||||
}>;
|
||||
}) {
|
||||
return await this.soClient.bulkUpdate<MonitorFields>(
|
||||
monitors.map(({ attributes, id }) => ({
|
||||
type: syntheticsMonitorType,
|
||||
id,
|
||||
attributes,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
find<T>(options: Omit<SavedObjectsFindOptions, 'type'>) {
|
||||
return this.soClient.find<T>({
|
||||
type: syntheticsMonitorType,
|
||||
...options,
|
||||
perPage: options.perPage ?? 5000,
|
||||
});
|
||||
}
|
||||
|
||||
async findDecryptedMonitors({ spaceId, filter }: { spaceId: string; filter?: string }) {
|
||||
const finder =
|
||||
await this.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
|
||||
{
|
||||
filter,
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 500,
|
||||
namespaces: [spaceId],
|
||||
}
|
||||
);
|
||||
|
||||
const decryptedMonitors: Array<SavedObjectsFindResult<SyntheticsMonitorWithSecretsAttributes>> =
|
||||
[];
|
||||
for await (const result of finder.find()) {
|
||||
decryptedMonitors.push(...result.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return decryptedMonitors;
|
||||
}
|
||||
|
||||
async delete(monitorId: string) {
|
||||
return this.soClient.delete(syntheticsMonitorType, monitorId);
|
||||
}
|
||||
|
||||
async bulkDelete(monitorIds: string[]) {
|
||||
return this.soClient.bulkDelete(
|
||||
monitorIds.map((monitor) => ({ type: syntheticsMonitorType, id: monitor }))
|
||||
);
|
||||
}
|
||||
|
||||
async getAll({
|
||||
search,
|
||||
fields,
|
||||
filter,
|
||||
sortField = 'name.keyword',
|
||||
sortOrder = 'asc',
|
||||
searchFields,
|
||||
showFromAllSpaces,
|
||||
}: {
|
||||
search?: string;
|
||||
filter?: string;
|
||||
showFromAllSpaces?: boolean;
|
||||
} & Pick<SavedObjectsFindOptions, 'sortField' | 'sortOrder' | 'fields' | 'searchFields'>) {
|
||||
return withApmSpan('get_all_monitors', async () => {
|
||||
const finder = this.soClient.createPointInTimeFinder<EncryptedSyntheticsMonitorAttributes>({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
search,
|
||||
sortField,
|
||||
sortOrder,
|
||||
fields,
|
||||
filter,
|
||||
searchFields,
|
||||
...(showFromAllSpaces && { namespaces: ['*'] }),
|
||||
});
|
||||
|
||||
const hits: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>> = [];
|
||||
for await (const result of finder.find()) {
|
||||
hits.push(...result.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return hits;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import { withApmSpan } from '@kbn/apm-data-access-plugin/server/utils/with_apm_s
|
|||
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isKibanaResponse } from '@kbn/core-http-server';
|
||||
import { MonitorConfigRepository } from './services/monitor_config_repository';
|
||||
import { syntheticsServiceApiKey } from './saved_objects/service_api_key';
|
||||
import { isTestUser, SyntheticsEsClient } from './lib';
|
||||
import { SYNTHETICS_INDEX_PATTERN } from '../common/constants';
|
||||
|
@ -56,6 +57,11 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = (
|
|||
);
|
||||
|
||||
server.syntheticsEsClient = syntheticsEsClient;
|
||||
const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
|
||||
const monitorConfigRepository = new MonitorConfigRepository(
|
||||
savedObjectsClient,
|
||||
encryptedSavedObjectsClient
|
||||
);
|
||||
|
||||
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
|
||||
|
||||
|
@ -69,6 +75,7 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = (
|
|||
server,
|
||||
spaceId,
|
||||
syntheticsMonitorClient,
|
||||
monitorConfigRepository,
|
||||
});
|
||||
if (isKibanaResponse(res)) {
|
||||
return res;
|
||||
|
|
|
@ -27,6 +27,7 @@ import { formatLocation } from '../../../common/utils/location_formatter';
|
|||
import * as locationsUtil from '../get_all_locations';
|
||||
import { mockEncryptedSO } from '../utils/mocks';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
import { MonitorConfigRepository } from '../../services/monitor_config_repository';
|
||||
|
||||
const testMonitors = [
|
||||
{
|
||||
|
@ -153,6 +154,7 @@ describe('ProjectMonitorFormatter', () => {
|
|||
server: serverMock,
|
||||
syntheticsMonitorClient: monitorClient,
|
||||
request: kibanaRequest,
|
||||
monitorConfigRepository: new MonitorConfigRepository(soClient, encryptedSavedObjectsClient),
|
||||
} as any;
|
||||
|
||||
jest.spyOn(locationsUtil, 'getAllLocations').mockImplementation(
|
||||
|
@ -203,7 +205,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
projectId: 'test-project',
|
||||
spaceId: 'default',
|
||||
routeContext,
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: [invalidMonitor],
|
||||
});
|
||||
|
||||
|
@ -239,7 +240,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: testMonitors,
|
||||
routeContext,
|
||||
});
|
||||
|
@ -271,7 +271,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: testMonitors,
|
||||
routeContext,
|
||||
});
|
||||
|
@ -303,7 +302,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: testMonitors,
|
||||
routeContext,
|
||||
});
|
||||
|
@ -341,7 +339,6 @@ describe('ProjectMonitorFormatter', () => {
|
|||
const pushMonitorFormatter = new ProjectMonitorFormatter({
|
||||
projectId: 'test-project',
|
||||
spaceId: 'default-space',
|
||||
encryptedSavedObjectsClient,
|
||||
monitors: testMonitors,
|
||||
routeContext,
|
||||
});
|
||||
|
|
|
@ -4,13 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
SavedObjectsUpdateResponse,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindResult,
|
||||
} from '@kbn/core/server';
|
||||
import { SavedObjectsUpdateResponse, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { getSavedObjectKqlFilter } from '../../routes/common';
|
||||
import { InvalidLocationError } from './normalizers/common_fields';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
|
@ -25,7 +20,6 @@ import {
|
|||
} from '../../routes/monitor_cruds/bulk_cruds/edit_monitor_bulk';
|
||||
import {
|
||||
ConfigKey,
|
||||
SyntheticsMonitorWithSecretsAttributes,
|
||||
EncryptedSyntheticsMonitorAttributes,
|
||||
ServiceLocationErrors,
|
||||
ProjectMonitor,
|
||||
|
@ -76,7 +70,6 @@ export class ProjectMonitorFormatter {
|
|||
private publicLocations: Locations;
|
||||
private privateLocations: SyntheticsPrivateLocations;
|
||||
private savedObjectsClient: SavedObjectsClientContract;
|
||||
private encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
|
||||
private monitors: ProjectMonitor[] = [];
|
||||
public createdMonitors: string[] = [];
|
||||
public updatedMonitors: string[] = [];
|
||||
|
@ -87,14 +80,12 @@ export class ProjectMonitorFormatter {
|
|||
private routeContext: RouteContext;
|
||||
|
||||
constructor({
|
||||
encryptedSavedObjectsClient,
|
||||
projectId,
|
||||
spaceId,
|
||||
monitors,
|
||||
routeContext,
|
||||
}: {
|
||||
routeContext: RouteContext;
|
||||
encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
|
||||
projectId: string;
|
||||
spaceId: string;
|
||||
monitors: ProjectMonitor[];
|
||||
|
@ -103,7 +94,6 @@ export class ProjectMonitorFormatter {
|
|||
this.projectId = projectId;
|
||||
this.spaceId = spaceId;
|
||||
this.savedObjectsClient = routeContext.savedObjectsClient;
|
||||
this.encryptedSavedObjectsClient = encryptedSavedObjectsClient;
|
||||
this.syntheticsMonitorClient = routeContext.syntheticsMonitorClient;
|
||||
this.monitors = monitors;
|
||||
this.server = routeContext.server;
|
||||
|
@ -262,9 +252,8 @@ export class ProjectMonitorFormatter {
|
|||
field: ConfigKey.JOURNEY_ID,
|
||||
values: journeyIds,
|
||||
});
|
||||
const finder = this.savedObjectsClient.createPointInTimeFinder<ExistingMonitor>({
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 5000,
|
||||
|
||||
const result = await this.routeContext.monitorConfigRepository.find<ExistingMonitor>({
|
||||
filter: `${this.projectFilter} AND ${journeyFilter}`,
|
||||
fields: [
|
||||
ConfigKey.JOURNEY_ID,
|
||||
|
@ -274,21 +263,12 @@ export class ProjectMonitorFormatter {
|
|||
],
|
||||
});
|
||||
|
||||
const hits: PreviousMonitorForUpdate[] = [];
|
||||
for await (const result of finder.find()) {
|
||||
hits.push(
|
||||
...result.saved_objects.map((monitor) => {
|
||||
return {
|
||||
...monitor.attributes,
|
||||
updated_at: monitor.updated_at,
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return hits;
|
||||
return result.saved_objects.map<PreviousMonitorForUpdate>((monitor) => {
|
||||
return {
|
||||
...monitor.attributes,
|
||||
updated_at: monitor.updated_at,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
private createMonitorsBulk = async (monitors: SyntheticsMonitor[]) => {
|
||||
|
@ -363,25 +343,11 @@ export class ProjectMonitorFormatter {
|
|||
field: ConfigKey.CONFIG_ID,
|
||||
values: configIds,
|
||||
});
|
||||
const finder =
|
||||
await this.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
|
||||
{
|
||||
filter: monitorFilter,
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 500,
|
||||
namespaces: [this.spaceId],
|
||||
}
|
||||
);
|
||||
|
||||
const decryptedMonitors: Array<SavedObjectsFindResult<SyntheticsMonitorWithSecretsAttributes>> =
|
||||
[];
|
||||
for await (const result of finder.find()) {
|
||||
decryptedMonitors.push(...result.saved_objects);
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return decryptedMonitors;
|
||||
return await this.routeContext.monitorConfigRepository.findDecryptedMonitors({
|
||||
filter: monitorFilter,
|
||||
spaceId: this.spaceId,
|
||||
});
|
||||
};
|
||||
|
||||
private updateMonitorsBulk = async (
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { SavedObjectsClientContract, CoreStart } from '@kbn/core/server';
|
||||
import { CoreStart } from '@kbn/core/server';
|
||||
import { coreMock } from '@kbn/core/server/mocks';
|
||||
import { SyntheticsMonitorClient } from './synthetics_monitor_client';
|
||||
import { SyntheticsService } from '../synthetics_service';
|
||||
|
@ -42,10 +42,6 @@ describe('SyntheticsMonitorClient', () => {
|
|||
const mockEsClient = {
|
||||
search: jest.fn(),
|
||||
};
|
||||
const savedObjectsClientMock = {
|
||||
bulkUpdate: jest.fn(),
|
||||
get: jest.fn(),
|
||||
} as unknown as SavedObjectsClientContract;
|
||||
|
||||
const logger = loggerMock.create();
|
||||
|
||||
|
@ -204,11 +200,7 @@ describe('SyntheticsMonitorClient', () => {
|
|||
client.privateLocationAPI.deleteMonitors = jest.fn();
|
||||
syntheticsService.deleteConfigs = jest.fn();
|
||||
|
||||
await client.deleteMonitors(
|
||||
[monitor as unknown as SyntheticsMonitorWithId],
|
||||
savedObjectsClientMock,
|
||||
'test-space'
|
||||
);
|
||||
await client.deleteMonitors([monitor as unknown as SyntheticsMonitorWithId], 'test-space');
|
||||
|
||||
expect(syntheticsService.deleteConfigs).toHaveBeenCalledTimes(1);
|
||||
expect(client.privateLocationAPI.deleteMonitors).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
import { SavedObject, SavedObjectsClientContract, SavedObjectsFindResult } from '@kbn/core/server';
|
||||
import { EncryptedSavedObjectsPluginStart } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { MonitorConfigRepository } from '../../services/monitor_config_repository';
|
||||
import { SyntheticsServerSetup } from '../../types';
|
||||
import { syntheticsMonitorType } from '../../../common/types/saved_objects';
|
||||
import { normalizeSecrets } from '../utils';
|
||||
import {
|
||||
PrivateConfig,
|
||||
|
@ -162,11 +162,7 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
return { failedPolicyUpdates, publicSyncErrors };
|
||||
}
|
||||
async deleteMonitors(
|
||||
monitors: SyntheticsMonitorWithId[],
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
spaceId: string
|
||||
) {
|
||||
async deleteMonitors(monitors: SyntheticsMonitorWithId[], spaceId: string) {
|
||||
const privateDeletePromise = this.privateLocationAPI.deleteMonitors(monitors, spaceId);
|
||||
|
||||
const publicDeletePromise = this.syntheticsService.deleteConfigs(
|
||||
|
@ -179,7 +175,6 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
async testNowConfigs(
|
||||
monitor: { monitor: MonitorFields; id: string; testRunId: string },
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
allPrivateLocations: PrivateLocationAttributes[],
|
||||
spaceId: string,
|
||||
runOnce?: true
|
||||
|
@ -274,8 +269,10 @@ export class SyntheticsMonitorClient {
|
|||
spaceId,
|
||||
allPrivateLocations,
|
||||
encryptedSavedObjects,
|
||||
soClient,
|
||||
}: {
|
||||
spaceId: string;
|
||||
soClient: SavedObjectsClientContract;
|
||||
allPrivateLocations: PrivateLocationAttributes[];
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}) {
|
||||
|
@ -285,6 +282,7 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
const { allConfigs: monitors, paramsBySpace } = await this.getAllMonitorConfigs({
|
||||
encryptedSavedObjects,
|
||||
soClient,
|
||||
spaceId,
|
||||
});
|
||||
|
||||
|
@ -309,14 +307,20 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
async getAllMonitorConfigs({
|
||||
spaceId,
|
||||
soClient,
|
||||
encryptedSavedObjects,
|
||||
}: {
|
||||
spaceId: string;
|
||||
soClient: SavedObjectsClientContract;
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}) {
|
||||
const paramsBySpacePromise = this.syntheticsService.getSyntheticsParams({ spaceId });
|
||||
const monitorConfigRepository = new MonitorConfigRepository(
|
||||
soClient,
|
||||
encryptedSavedObjects.getClient()
|
||||
);
|
||||
|
||||
const monitorsPromise = this.getAllMonitors({ encryptedSavedObjects, spaceId });
|
||||
const monitorsPromise = monitorConfigRepository.findDecryptedMonitors({ spaceId });
|
||||
|
||||
const [paramsBySpace, monitors] = await Promise.all([paramsBySpacePromise, monitorsPromise]);
|
||||
|
||||
|
@ -326,37 +330,6 @@ export class SyntheticsMonitorClient {
|
|||
};
|
||||
}
|
||||
|
||||
async getAllMonitors({
|
||||
spaceId,
|
||||
encryptedSavedObjects,
|
||||
}: {
|
||||
spaceId: string;
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}) {
|
||||
const encryptedClient = encryptedSavedObjects.getClient();
|
||||
|
||||
const monitors: Array<SavedObjectsFindResult<SyntheticsMonitorWithSecretsAttributes>> = [];
|
||||
|
||||
const finder =
|
||||
await encryptedClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
|
||||
{
|
||||
type: syntheticsMonitorType,
|
||||
perPage: 1000,
|
||||
namespaces: [spaceId],
|
||||
}
|
||||
);
|
||||
|
||||
for await (const response of finder.find()) {
|
||||
response.saved_objects.forEach((monitor) => {
|
||||
monitors.push(monitor);
|
||||
});
|
||||
}
|
||||
|
||||
finder.close().catch(() => {});
|
||||
|
||||
return monitors;
|
||||
}
|
||||
|
||||
mixParamsWithMonitors(
|
||||
spaceId: string,
|
||||
monitors: Array<SavedObjectsFindResult<SyntheticsMonitorWithSecretsAttributes>>,
|
||||
|
|
|
@ -29,11 +29,10 @@ export function normalizeSecrets(
|
|||
monitor: SavedObject<SyntheticsMonitorWithSecretsAttributes | SyntheticsMonitor880>
|
||||
): SavedObject<SyntheticsMonitor> {
|
||||
const attributes = normalizeMonitorSecretAttributes(monitor.attributes);
|
||||
const normalizedMonitor = {
|
||||
return {
|
||||
...monitor,
|
||||
attributes,
|
||||
};
|
||||
return normalizedMonitor;
|
||||
}
|
||||
|
||||
export function normalizeMonitorSecretAttributes(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue