From d08df9ece4c977f582626bf6dd6c4975feae2199 Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Wed, 1 Dec 2021 16:17:11 -0600 Subject: [PATCH 01/20] [Security Solution] remove POST metadata list API (#119401) --- .../common/endpoint/schema/metadata.test.ts | 4 +- .../common/endpoint/schema/metadata.ts | 4 +- .../management/pages/endpoint_hosts/mocks.ts | 24 - .../endpoint/routes/metadata/handlers.ts | 90 +-- .../server/endpoint/routes/metadata/index.ts | 54 +- .../endpoint/routes/metadata/metadata.test.ts | 513 +---------------- .../apis/metadata.ts | 540 ------------------ 7 files changed, 12 insertions(+), 1217 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/metadata.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/metadata.test.ts index b9693fde1d659b..fa5104da8dab6a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/metadata.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/metadata.test.ts @@ -7,11 +7,11 @@ import { ENDPOINT_DEFAULT_PAGE, ENDPOINT_DEFAULT_PAGE_SIZE } from '../constants'; import { HostStatus } from '../types'; -import { GetMetadataListRequestSchemaV2 } from './metadata'; +import { GetMetadataListRequestSchema } from './metadata'; describe('endpoint metadata schema', () => { describe('GetMetadataListRequestSchemaV2', () => { - const query = GetMetadataListRequestSchemaV2.query; + const query = GetMetadataListRequestSchema.query; it('should return correct query params when valid', () => { const queryParams = { diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/metadata.ts b/x-pack/plugins/security_solution/common/endpoint/schema/metadata.ts index eb123590af20cb..4271b8417a9849 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/metadata.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/metadata.ts @@ -9,7 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { ENDPOINT_DEFAULT_PAGE, ENDPOINT_DEFAULT_PAGE_SIZE } from '../constants'; import { HostStatus } from '../types'; -export const GetMetadataListRequestSchemaV2 = { +export const GetMetadataListRequestSchema = { query: schema.object( { page: schema.number({ defaultValue: ENDPOINT_DEFAULT_PAGE, min: 0 }), @@ -31,4 +31,4 @@ export const GetMetadataListRequestSchemaV2 = { ), }; -export type GetMetadataListRequestQuery = TypeOf; +export type GetMetadataListRequestQuery = TypeOf; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts index 781c332430c0fe..3aacd1db2f3dd9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts @@ -48,30 +48,6 @@ type EndpointMetadataHttpMocksInterface = ResponseProvidersInterface<{ }>; export const endpointMetadataHttpMocks = httpHandlerMockFactory( [ - { - id: 'metadataList', - path: HOST_METADATA_LIST_ROUTE, - method: 'post', - handler: () => { - const generator = new EndpointDocGenerator('seed'); - - return { - hosts: Array.from({ length: 10 }, () => { - const endpoint = { - metadata: generator.generateHostMetadata(), - host_status: HostStatus.UNHEALTHY, - }; - - generator.updateCommonInfo(); - - return endpoint; - }), - total: 10, - request_page_size: 10, - request_page_index: 0, - }; - }, - }, { id: 'metadataList', path: HOST_METADATA_LIST_ROUTE, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index 708cac5a845ce5..06e63c6b7ec59c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -23,11 +23,11 @@ import { } from '../../../../common/endpoint/types'; import type { SecuritySolutionRequestHandlerContext } from '../../../types'; -import { getPagingProperties, kibanaRequestToMetadataListESQuery } from './query_builders'; +import { kibanaRequestToMetadataListESQuery } from './query_builders'; import { PackagePolicy } from '../../../../../fleet/common/types/models'; import { AgentNotFoundError } from '../../../../../fleet/server'; import { EndpointAppContext, HostListQueryResult } from '../../types'; -import { GetMetadataListRequestSchema, GetMetadataRequestSchema } from './index'; +import { GetMetadataRequestSchema } from './index'; import { findAllUnenrolledAgentIds } from './support/unenroll'; import { getAllEndpointPackagePolicies } from './support/endpoint_package_policies'; import { findAgentIdsByStatus } from './support/agent_status'; @@ -82,91 +82,7 @@ const errorHandler = ( throw error; }; -export const getMetadataListRequestHandler = function ( - endpointAppContext: EndpointAppContext, - logger: Logger -): RequestHandler< - unknown, - unknown, - TypeOf, - SecuritySolutionRequestHandlerContext -> { - return async (context, request, response) => { - const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService(); - const fleetServices = endpointAppContext.service.getScopedFleetServices(request); - - let doesUnitedIndexExist = false; - let didUnitedIndexError = false; - let body: HostResultList = { - hosts: [], - total: 0, - request_page_size: 0, - request_page_index: 0, - }; - - try { - doesUnitedIndexExist = await endpointMetadataService.doesUnitedIndexExist( - context.core.elasticsearch.client.asCurrentUser - ); - } catch (error) { - // for better UX, try legacy query instead of immediately failing on united index error - didUnitedIndexError = true; - } - - // If no unified Index present, then perform a search using the legacy approach - if (!doesUnitedIndexExist || didUnitedIndexError) { - const endpointPolicies = await getAllEndpointPackagePolicies( - fleetServices.packagePolicy, - context.core.savedObjects.client - ); - - const pagingProperties = await getPagingProperties(request, endpointAppContext); - - body = await legacyListMetadataQuery( - context, - endpointAppContext, - fleetServices, - logger, - endpointPolicies, - { - page: pagingProperties.pageIndex, - pageSize: pagingProperties.pageSize, - kuery: request?.body?.filters?.kql || '', - hostStatuses: request?.body?.filters?.host_status || [], - } - ); - return response.ok({ body }); - } - - // Unified index is installed and being used - perform search using new approach - try { - const pagingProperties = await getPagingProperties(request, endpointAppContext); - const { data, total } = await endpointMetadataService.getHostMetadataList( - context.core.elasticsearch.client.asCurrentUser, - fleetServices, - { - page: pagingProperties.pageIndex, - pageSize: pagingProperties.pageSize, - hostStatuses: request.body?.filters.host_status || [], - kuery: request.body?.filters.kql || '', - } - ); - - body = { - hosts: data, - total, - request_page_index: pagingProperties.pageIndex * pagingProperties.pageSize, - request_page_size: pagingProperties.pageSize, - }; - } catch (error) { - return errorHandler(logger, response, error); - } - - return response.ok({ body }); - }; -}; - -export function getMetadataListRequestHandlerV2( +export function getMetadataListRequestHandler( endpointAppContext: EndpointAppContext, logger: Logger ): RequestHandler< diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts index ffa0bf06379005..6cd1ae275d592d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts @@ -9,20 +9,13 @@ import { schema } from '@kbn/config-schema'; import { HostStatus } from '../../../../common/endpoint/types'; import { EndpointAppContext } from '../../types'; -import { - getLogger, - getMetadataListRequestHandler, - getMetadataRequestHandler, - getMetadataListRequestHandlerV2, -} from './handlers'; +import { getLogger, getMetadataRequestHandler, getMetadataListRequestHandler } from './handlers'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { - ENDPOINT_DEFAULT_PAGE, - ENDPOINT_DEFAULT_PAGE_SIZE, HOST_METADATA_GET_ROUTE, HOST_METADATA_LIST_ROUTE, } from '../../../../common/endpoint/constants'; -import { GetMetadataListRequestSchemaV2 } from '../../../../common/endpoint/schema/metadata'; +import { GetMetadataListRequestSchema } from '../../../../common/endpoint/schema/metadata'; /* Filters that can be applied to the endpoint fetch route */ export const endpointFilters = schema.object({ @@ -44,36 +37,6 @@ export const GetMetadataRequestSchema = { params: schema.object({ id: schema.string() }), }; -export const GetMetadataListRequestSchema = { - body: schema.nullable( - schema.object({ - paging_properties: schema.nullable( - schema.arrayOf( - schema.oneOf([ - /** - * the number of results to return for this request per page - */ - schema.object({ - page_size: schema.number({ - defaultValue: ENDPOINT_DEFAULT_PAGE_SIZE, - min: 1, - max: 10000, - }), - }), - /** - * the zero based page index of the the total number of pages of page size - */ - schema.object({ - page_index: schema.number({ defaultValue: ENDPOINT_DEFAULT_PAGE, min: 0 }), - }), - ]) - ) - ), - filters: endpointFilters, - }) - ), -}; - export function registerEndpointRoutes( router: SecuritySolutionPluginRouter, endpointAppContext: EndpointAppContext @@ -83,10 +46,10 @@ export function registerEndpointRoutes( router.get( { path: HOST_METADATA_LIST_ROUTE, - validate: GetMetadataListRequestSchemaV2, + validate: GetMetadataListRequestSchema, options: { authRequired: true, tags: ['access:securitySolution'] }, }, - getMetadataListRequestHandlerV2(endpointAppContext, logger) + getMetadataListRequestHandler(endpointAppContext, logger) ); router.get( @@ -97,13 +60,4 @@ export function registerEndpointRoutes( }, getMetadataRequestHandler(endpointAppContext, logger) ); - - router.post( - { - path: HOST_METADATA_LIST_ROUTE, - validate: GetMetadataListRequestSchema, - options: { authRequired: true, tags: ['access:securitySolution'] }, - }, - getMetadataListRequestHandler(endpointAppContext, logger) - ); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index c705246014a7b6..e324f66ad38f6c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -18,12 +18,7 @@ import { loggingSystemMock, savedObjectsClientMock, } from '../../../../../../../src/core/server/mocks'; -import { - HostInfo, - HostResultList, - HostStatus, - MetadataListResponse, -} from '../../../../common/endpoint/types'; +import { HostInfo, HostStatus, MetadataListResponse } from '../../../../common/endpoint/types'; import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; import { registerEndpointRoutes } from './index'; import { @@ -121,512 +116,6 @@ describe('test endpoint routes', () => { }); }); - describe('POST list endpoints route', () => { - describe('with .metrics-endpoint.metadata_united_default index', () => { - beforeEach(() => { - endpointAppContextService = new EndpointAppContextService(); - mockPackageService = createMockPackageService(); - mockPackageService.getInstallation.mockReturnValue( - Promise.resolve({ - installed_kibana: [], - package_assets: [], - es_index_patterns: {}, - name: '', - version: '', - install_status: 'installed', - install_version: '', - install_started_at: '', - install_source: 'registry', - installed_es: [ - { - id: 'logs-endpoint.events.security', - type: ElasticsearchAssetType.indexTemplate, - }, - { - id: `${metadataTransformPrefix}-0.16.0-dev.0`, - type: ElasticsearchAssetType.transform, - }, - ], - keep_policies_up_to_date: false, - }) - ); - endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); - endpointAppContextService.start({ ...startContract, packageService: mockPackageService }); - mockAgentService = startContract.agentService!; - mockAgentClient = createMockAgentClient(); - mockAgentService.asScoped = () => mockAgentClient; - mockAgentPolicyService = startContract.agentPolicyService!; - - registerEndpointRoutes(routerMock, { - logFactory: loggingSystemMock.create(), - service: endpointAppContextService, - config: () => Promise.resolve(createMockConfig()), - experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental), - }); - }); - - afterEach(() => endpointAppContextService.stop()); - - it('should fallback to legacy index if index not found', async () => { - const mockRequest = httpServerMock.createKibanaRequest({}); - const response = legacyMetadataSearchResponseMock( - new EndpointDocGenerator().generateHostMetadata() - ); - (mockScopedClient.asCurrentUser.search as jest.Mock) - .mockImplementationOnce(() => { - throw new IndexNotFoundException(); - }) - .mockImplementationOnce(() => Promise.resolve({ body: response })); - [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => - path.startsWith(HOST_METADATA_LIST_ROUTE) - )!; - mockAgentClient.getAgentStatusById.mockResolvedValue('error'); - mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent); - await routeHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), - mockRequest, - mockResponse - ); - - const esSearchMock = mockScopedClient.asCurrentUser.search; - // should be called twice, united index first, then legacy index - expect(esSearchMock).toHaveBeenCalledTimes(2); - expect(esSearchMock.mock.calls[0][0]?.index).toEqual(METADATA_UNITED_INDEX); - expect(esSearchMock.mock.calls[1][0]?.index).toEqual(metadataCurrentIndexPattern); - expect(routeConfig.options).toEqual({ - authRequired: true, - tags: ['access:securitySolution'], - }); - expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; - expect(endpointResultList.hosts.length).toEqual(1); - expect(endpointResultList.total).toEqual(1); - expect(endpointResultList.request_page_index).toEqual(0); - expect(endpointResultList.request_page_size).toEqual(10); - }); - - it('should return expected metadata', async () => { - const mockRequest = httpServerMock.createKibanaRequest({ - body: { - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 0, - }, - ], - - filters: { - kql: 'not host.ip:10.140.73.246', - host_status: ['updating'], - }, - }, - }); - - mockAgentClient.getAgentStatusById.mockResolvedValue('error'); - mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent); - mockAgentPolicyService.getByIds = jest.fn().mockResolvedValueOnce([]); - const metadata = new EndpointDocGenerator().generateHostMetadata(); - const esSearchMock = mockScopedClient.asCurrentUser.search as jest.Mock; - esSearchMock.mockResolvedValueOnce({}); - esSearchMock.mockResolvedValueOnce({ - body: unitedMetadataSearchResponseMock(metadata), - }); - [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => - path.startsWith(HOST_METADATA_LIST_ROUTE) - )!; - - await routeHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), - mockRequest, - mockResponse - ); - - expect(esSearchMock).toHaveBeenCalledTimes(2); - expect(esSearchMock.mock.calls[0][0]?.index).toEqual(METADATA_UNITED_INDEX); - expect(esSearchMock.mock.calls[0][0]?.size).toEqual(1); - expect(esSearchMock.mock.calls[1][0]?.index).toEqual(METADATA_UNITED_INDEX); - expect(esSearchMock.mock.calls[1][0]?.body?.query).toEqual({ - bool: { - must: [ - { - bool: { - filter: [ - { - terms: { - 'united.agent.policy_id': [], - }, - }, - { - exists: { - field: 'united.endpoint.agent.id', - }, - }, - { - exists: { - field: 'united.agent.agent.id', - }, - }, - { - term: { - 'united.agent.active': { - value: true, - }, - }, - }, - ], - must_not: { - terms: { - 'agent.id': [ - '00000000-0000-0000-0000-000000000000', - '11111111-1111-1111-1111-111111111111', - ], - }, - }, - }, - }, - { - bool: { - should: [ - { - bool: { - filter: [ - { - bool: { - should: [ - { - exists: { - field: 'united.agent.upgrade_started_at', - }, - }, - ], - minimum_should_match: 1, - }, - }, - { - bool: { - must_not: { - bool: { - should: [ - { - exists: { - field: 'united.agent.upgraded_at', - }, - }, - ], - minimum_should_match: 1, - }, - }, - }, - }, - ], - }, - }, - { - bool: { - must_not: { - bool: { - should: [ - { - exists: { - field: 'united.agent.last_checkin', - }, - }, - ], - minimum_should_match: 1, - }, - }, - }, - }, - { - bool: { - should: [ - { - exists: { - field: 'united.agent.unenrollment_started_at', - }, - }, - ], - minimum_should_match: 1, - }, - }, - ], - minimum_should_match: 1, - }, - }, - { - bool: { - must_not: { - bool: { - should: [ - { - match: { - 'host.ip': '10.140.73.246', - }, - }, - ], - minimum_should_match: 1, - }, - }, - }, - }, - ], - }, - }); - expect(routeConfig.options).toEqual({ - authRequired: true, - tags: ['access:securitySolution'], - }); - expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; - expect(endpointResultList.hosts.length).toEqual(1); - expect(endpointResultList.hosts[0].metadata).toEqual(metadata); - expect(endpointResultList.total).toEqual(1); - expect(endpointResultList.request_page_index).toEqual(0); - expect(endpointResultList.request_page_size).toEqual(10); - }); - }); - - describe('with metrics-endpoint.metadata_current_default index', () => { - beforeEach(() => { - endpointAppContextService = new EndpointAppContextService(); - mockPackageService = createMockPackageService(); - mockPackageService.getInstallation.mockReturnValue( - Promise.resolve({ - installed_kibana: [], - package_assets: [], - es_index_patterns: {}, - name: '', - version: '', - install_status: 'installed', - install_version: '', - install_started_at: '', - install_source: 'registry', - installed_es: [ - { - id: 'logs-endpoint.events.security', - type: ElasticsearchAssetType.indexTemplate, - }, - { - id: `${metadataTransformPrefix}-0.16.0-dev.0`, - type: ElasticsearchAssetType.transform, - }, - ], - keep_policies_up_to_date: false, - }) - ); - endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); - endpointAppContextService.start({ ...startContract, packageService: mockPackageService }); - mockAgentService = startContract.agentService!; - mockAgentClient = createMockAgentClient(); - mockAgentService.asScoped = () => mockAgentClient; - - registerEndpointRoutes(routerMock, { - logFactory: loggingSystemMock.create(), - service: endpointAppContextService, - config: () => Promise.resolve(createMockConfig()), - experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental), - }); - }); - - afterEach(() => endpointAppContextService.stop()); - - it('test find the latest of all endpoints', async () => { - const mockRequest = httpServerMock.createKibanaRequest({}); - const response = legacyMetadataSearchResponseMock( - new EndpointDocGenerator().generateHostMetadata() - ); - (mockScopedClient.asCurrentUser.search as jest.Mock) - .mockImplementationOnce(() => { - throw new IndexNotFoundException(); - }) - .mockImplementationOnce(() => Promise.resolve({ body: response })); - [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => - path.startsWith(HOST_METADATA_LIST_ROUTE) - )!; - mockAgentClient.getAgentStatusById.mockResolvedValue('error'); - mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent); - await routeHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), - mockRequest, - mockResponse - ); - - expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(2); - expect(routeConfig.options).toEqual({ - authRequired: true, - tags: ['access:securitySolution'], - }); - expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; - expect(endpointResultList.hosts.length).toEqual(1); - expect(endpointResultList.total).toEqual(1); - expect(endpointResultList.request_page_index).toEqual(0); - expect(endpointResultList.request_page_size).toEqual(10); - }); - - it('test find the latest of all endpoints with paging properties', async () => { - const mockRequest = httpServerMock.createKibanaRequest({ - body: { - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 1, - }, - ], - }, - }); - - mockAgentClient.getAgentStatusById.mockResolvedValue('error'); - mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent); - (mockScopedClient.asCurrentUser.search as jest.Mock) - .mockImplementationOnce(() => { - throw new IndexNotFoundException(); - }) - .mockImplementationOnce(() => - Promise.resolve({ - body: legacyMetadataSearchResponseMock( - new EndpointDocGenerator().generateHostMetadata() - ), - }) - ); - [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => - path.startsWith(HOST_METADATA_LIST_ROUTE) - )!; - - await routeHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), - mockRequest, - mockResponse - ); - expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(2); - expect( - (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[1][0]?.body?.query.bool - .must_not - ).toContainEqual({ - terms: { - 'elastic.agent.id': [ - '00000000-0000-0000-0000-000000000000', - '11111111-1111-1111-1111-111111111111', - ], - }, - }); - expect(routeConfig.options).toEqual({ - authRequired: true, - tags: ['access:securitySolution'], - }); - expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; - expect(endpointResultList.hosts.length).toEqual(1); - expect(endpointResultList.total).toEqual(1); - expect(endpointResultList.request_page_index).toEqual(10); - expect(endpointResultList.request_page_size).toEqual(10); - }); - - it('test find the latest of all endpoints with paging and filters properties', async () => { - const mockRequest = httpServerMock.createKibanaRequest({ - body: { - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 1, - }, - ], - - filters: { kql: 'not host.ip:10.140.73.246' }, - }, - }); - - mockAgentClient.getAgentStatusById.mockResolvedValue('error'); - mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent); - (mockScopedClient.asCurrentUser.search as jest.Mock) - .mockImplementationOnce(() => { - throw new IndexNotFoundException(); - }) - .mockImplementationOnce(() => - Promise.resolve({ - body: legacyMetadataSearchResponseMock( - new EndpointDocGenerator().generateHostMetadata() - ), - }) - ); - [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => - path.startsWith(HOST_METADATA_LIST_ROUTE) - )!; - - await routeHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), - mockRequest, - mockResponse - ); - - expect(mockScopedClient.asCurrentUser.search).toBeCalled(); - expect( - // KQL filter to be passed through - (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[1][0]?.body?.query.bool - .must - ).toContainEqual({ - bool: { - must_not: { - bool: { - should: [ - { - match: { - 'host.ip': '10.140.73.246', - }, - }, - ], - minimum_should_match: 1, - }, - }, - }, - }); - expect( - (mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[1][0]?.body?.query.bool - .must - ).toContainEqual({ - bool: { - must_not: [ - { - terms: { - 'elastic.agent.id': [ - '00000000-0000-0000-0000-000000000000', - '11111111-1111-1111-1111-111111111111', - ], - }, - }, - { - terms: { - // here we DO want to see both schemas are present - // to make this schema-compatible forward and back - 'HostDetails.elastic.agent.id': [ - '00000000-0000-0000-0000-000000000000', - '11111111-1111-1111-1111-111111111111', - ], - }, - }, - ], - }, - }); - expect(routeConfig.options).toEqual({ - authRequired: true, - tags: ['access:securitySolution'], - }); - expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; - expect(endpointResultList.hosts.length).toEqual(1); - expect(endpointResultList.total).toEqual(1); - expect(endpointResultList.request_page_index).toEqual(10); - expect(endpointResultList.request_page_size).toEqual(10); - }); - }); - }); - describe('GET list endpoints route', () => { describe('with .metrics-endpoint.metadata_united_default index', () => { beforeEach(() => { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index 471d00728bac3e..93f3756fc111cf 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -33,546 +33,6 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); describe('test metadata apis', () => { - describe('list endpoints POST route', () => { - describe('with .metrics-endpoint.metadata_united_default index', () => { - const numberOfHostsInFixture = 2; - - before(async () => { - await stopTransform(getService, `${METADATA_UNITED_TRANSFORM}*`); - await deleteAllDocsFromFleetAgents(getService); - await deleteAllDocsFromMetadataDatastream(getService); - await deleteAllDocsFromMetadataCurrentIndex(getService); - await deleteAllDocsFromIndex(getService, METADATA_UNITED_INDEX); - - // generate an endpoint policy and attach id to agents since - // metadata list api filters down to endpoint policies only - const policy = await indexFleetEndpointPolicy( - getService('kibanaServer'), - `Default ${uuid.v4()}`, - '1.1.1' - ); - const policyId = policy.integrationPolicies[0].policy_id; - const currentTime = new Date().getTime(); - - await Promise.all([ - bulkIndex(getService, AGENTS_INDEX, generateAgentDocs(currentTime, policyId)), - bulkIndex(getService, METADATA_DATASTREAM, generateMetadataDocs(currentTime)), - ]); - - // wait for latest metadata transform to run - await new Promise((r) => setTimeout(r, 30000)); - await startTransform(getService, METADATA_UNITED_TRANSFORM); - - // wait for united metadata transform to run - await new Promise((r) => setTimeout(r, 15000)); - }); - - after(async () => { - await deleteAllDocsFromFleetAgents(getService); - await deleteAllDocsFromMetadataDatastream(getService); - await deleteAllDocsFromMetadataCurrentIndex(getService); - await stopTransform(getService, `${METADATA_UNITED_TRANSFORM}*`); - await deleteAllDocsFromIndex(getService, METADATA_UNITED_INDEX); - }); - - it('should return one entry for each host with default paging', async () => { - const res = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send() - .expect(200); - const { body } = res; - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(numberOfHostsInFixture); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on paging properties passed.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 1, - }, - { - page_index: 1, - }, - ], - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(1); - expect(body.request_page_index).to.eql(1); - }); - - it('metadata api should return accurate total metadata if page index produces no result', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 3, - }, - ], - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(0); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(30); - }); - - it('metadata api should return 400 when pagingProperties is below boundaries.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 0, - }, - { - page_index: 1, - }, - ], - }) - .expect(400); - expect(body.message).to.contain('Value must be equal to or greater than [1]'); - }); - - it('metadata api should return page based on filters passed.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: 'not (united.endpoint.host.ip:10.101.149.26)', - }, - }) - .expect(200); - expect(body.total).to.eql(1); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on filters and paging passed.', async () => { - const notIncludedIp = '10.101.149.26'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 0, - }, - ], - filters: { - kql: `not (united.endpoint.host.ip:${notIncludedIp})`, - }, - }) - .expect(200); - expect(body.total).to.eql(1); - const resultIps: string[] = [].concat( - ...body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.ip) - ); - expect(resultIps.sort()).to.eql(['10.192.213.130', '10.70.28.129'].sort()); - expect(resultIps).not.include.eql(notIncludedIp); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on host.os.Ext.variant filter.', async () => { - const variantValue = 'Windows Pro'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `united.endpoint.host.os.Ext.variant:${variantValue}`, - }, - }) - .expect(200); - expect(body.total).to.eql(2); - const resultOsVariantValue: Set = new Set( - body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.os.Ext.variant) - ); - expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); - expect(body.hosts.length).to.eql(2); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return the latest event for all the events for an endpoint', async () => { - const targetEndpointIp = '10.101.149.26'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `united.endpoint.host.ip:${targetEndpointIp}`, - }, - }) - .expect(200); - expect(body.total).to.eql(1); - const resultIp: string = body.hosts[0].metadata.host.ip.filter( - (ip: string) => ip === targetEndpointIp - ); - expect(resultIp).to.eql([targetEndpointIp]); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return the latest event for all the events where policy status is not success', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `not (united.endpoint.Endpoint.policy.applied.status:success)`, - }, - }) - .expect(200); - const statuses: Set = new Set( - body.hosts.map( - (hostInfo: Record) => hostInfo.metadata.Endpoint.policy.applied.status - ) - ); - expect(statuses.size).to.eql(1); - expect(Array.from(statuses)).to.eql(['failure']); - }); - - it('metadata api should return the endpoint based on the elastic agent id, and status should be healthy', async () => { - const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf'; - const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `united.endpoint.elastic.agent.id:${targetElasticAgentId}`, - }, - }) - .expect(200); - expect(body.total).to.eql(1); - const resultHostId: string = body.hosts[0].metadata.host.id; - const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id; - expect(resultHostId).to.eql(targetEndpointId); - expect(resultElasticAgentId).to.eql(targetElasticAgentId); - expect(body.hosts[0].host_status).to.eql('healthy'); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return all hosts when filter is empty string', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: '', - }, - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(numberOfHostsInFixture); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - }); - - describe('with metrics-endpoint.metadata_current_default index', () => { - /** - * The number of host documents in the es archive. - */ - const numberOfHostsInFixture = 3; - - describe(`POST ${HOST_METADATA_LIST_ROUTE} when index is empty`, () => { - it('metadata api should return empty result when index is empty', async () => { - await stopTransform(getService, `${METADATA_UNITED_TRANSFORM}*`); - await deleteIndex(getService, METADATA_UNITED_INDEX); - await deleteMetadataStream(getService); - await deleteAllDocsFromMetadataDatastream(getService); - await deleteAllDocsFromMetadataCurrentIndex(getService); - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send() - .expect(200); - expect(body.total).to.eql(0); - expect(body.hosts.length).to.eql(0); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - }); - - describe(`POST ${HOST_METADATA_LIST_ROUTE} when index is not empty`, () => { - const timestamp = new Date().getTime(); - before(async () => { - // stop the united transform and delete the index - // otherwise it won't hit metrics-endpoint.metadata_current_default index - await stopTransform(getService, `${METADATA_UNITED_TRANSFORM}*`); - await deleteIndex(getService, METADATA_UNITED_INDEX); - await bulkIndex(getService, METADATA_DATASTREAM, generateMetadataDocs(timestamp)); - // wait for transform - await new Promise((r) => setTimeout(r, 60000)); - }); - // the endpoint uses data streams and es archiver does not support deleting them at the moment so we need - // to do it manually - after(async () => { - await deleteMetadataStream(getService); - await deleteAllDocsFromMetadataDatastream(getService); - await deleteAllDocsFromMetadataCurrentIndex(getService); - }); - it('metadata api should return one entry for each host with default paging', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send() - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(numberOfHostsInFixture); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on paging properties passed.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 1, - }, - { - page_index: 1, - }, - ], - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(1); - expect(body.request_page_index).to.eql(1); - }); - - /* test that when paging properties produces no result, the total should reflect the actual number of metadata - in the index. - */ - it('metadata api should return accurate total metadata if page index produces no result', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 3, - }, - ], - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(0); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(30); - }); - - it('metadata api should return 400 when pagingProperties is below boundaries.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 0, - }, - { - page_index: 1, - }, - ], - }) - .expect(400); - expect(body.message).to.contain('Value must be equal to or greater than [1]'); - }); - - it('metadata api should return page based on filters passed.', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: 'not (HostDetails.host.ip:10.46.229.234 or host.ip:10.46.229.234)', - }, - }) - .expect(200); - expect(body.total).to.eql(2); - expect(body.hosts.length).to.eql(2); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on filters and paging passed.', async () => { - const notIncludedIp = '10.46.229.234'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - paging_properties: [ - { - page_size: 10, - }, - { - page_index: 0, - }, - ], - filters: { - kql: `not (HostDetails.host.ip:${notIncludedIp} or host.ip:${notIncludedIp})`, - }, - }) - .expect(200); - expect(body.total).to.eql(2); - const resultIps: string[] = [].concat( - ...body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.ip) - ); - expect(resultIps.sort()).to.eql( - [ - '10.192.213.130', - '10.70.28.129', - '10.101.149.26', - '2606:a000:ffc0:39:11ef:37b9:3371:578c', - ].sort() - ); - expect(resultIps).not.include.eql(notIncludedIp); - expect(body.hosts.length).to.eql(2); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return page based on host.os.Ext.variant filter.', async () => { - const variantValue = 'Windows Pro'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `HostDetails.host.os.Ext.variant:${variantValue} or host.os.Ext.variant:${variantValue}`, - }, - }) - .expect(200); - expect(body.total).to.eql(2); - const resultOsVariantValue: Set = new Set( - body.hosts.map( - (hostInfo: Record) => hostInfo.metadata.host.os.Ext.variant - ) - ); - expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); - expect(body.hosts.length).to.eql(2); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return the latest event for all the events for an endpoint', async () => { - const targetEndpointIp = '10.46.229.234'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `HostDetails.host.ip:${targetEndpointIp} or host.ip:${targetEndpointIp}`, - }, - }) - .expect(200); - expect(body.total).to.eql(1); - const resultIp: string = body.hosts[0].metadata.host.ip.filter( - (ip: string) => ip === targetEndpointIp - ); - expect(resultIp).to.eql([targetEndpointIp]); - expect(body.hosts[0].metadata.event.created).to.eql(timestamp); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return the latest event for all the events where policy status is not success', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `not (HostDetails.Endpoint.policy.applied.status:success or Endpoint.policy.applied.status:success)`, - }, - }) - .expect(200); - const statuses: Set = new Set( - body.hosts.map( - (hostInfo: Record) => hostInfo.metadata.Endpoint.policy.applied.status - ) - ); - expect(statuses.size).to.eql(1); - expect(Array.from(statuses)).to.eql(['failure']); - }); - - it('metadata api should return the endpoint based on the elastic agent id, and status should be unhealthy', async () => { - const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf'; - const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095'; - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: `HostDetails.elastic.agent.id:${targetElasticAgentId} or elastic.agent.id:${targetElasticAgentId}`, - }, - }) - .expect(200); - expect(body.total).to.eql(1); - const resultHostId: string = body.hosts[0].metadata.host.id; - const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id; - expect(resultHostId).to.eql(targetEndpointId); - expect(resultElasticAgentId).to.eql(targetElasticAgentId); - expect(body.hosts[0].metadata.event.created).to.eql(timestamp); - expect(body.hosts[0].host_status).to.eql('unhealthy'); - expect(body.hosts.length).to.eql(1); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - - it('metadata api should return all hosts when filter is empty string', async () => { - const { body } = await supertest - .post(HOST_METADATA_LIST_ROUTE) - .set('kbn-xsrf', 'xxx') - .send({ - filters: { - kql: '', - }, - }) - .expect(200); - expect(body.total).to.eql(numberOfHostsInFixture); - expect(body.hosts.length).to.eql(numberOfHostsInFixture); - expect(body.request_page_size).to.eql(10); - expect(body.request_page_index).to.eql(0); - }); - }); - }); - }); - describe('list endpoints GET route', () => { describe('with .metrics-endpoint.metadata_united_default index', () => { const numberOfHostsInFixture = 2; From 8d16f6086eaa94b18b67f7919e594f3842978120 Mon Sep 17 00:00:00 2001 From: mgiota Date: Wed, 1 Dec 2021 23:28:50 +0100 Subject: [PATCH 02/20] remove kibana.consumers from technical field names (#120173) --- packages/kbn-rule-data-utils/src/technical_field_names.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index 49e1397d10f970..349719c019c221 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -13,7 +13,6 @@ const KIBANA_NAMESPACE = 'kibana' as const; const ALERT_NAMESPACE = `${KIBANA_NAMESPACE}.alert` as const; const ALERT_RULE_NAMESPACE = `${ALERT_NAMESPACE}.rule` as const; -const CONSUMERS = `${KIBANA_NAMESPACE}.consumers` as const; const ECS_VERSION = 'ecs.version' as const; const EVENT_ACTION = 'event.action' as const; const EVENT_KIND = 'event.kind' as const; @@ -85,7 +84,6 @@ const namespaces = { }; const fields = { - CONSUMERS, ECS_VERSION, EVENT_KIND, EVENT_ACTION, @@ -187,7 +185,6 @@ export { ALERT_START, ALERT_SYSTEM_STATUS, ALERT_UUID, - CONSUMERS, ECS_VERSION, EVENT_ACTION, EVENT_KIND, From 2070fcb261c6a8c05ff5bbc301a009d42166258e Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 1 Dec 2021 16:50:12 -0600 Subject: [PATCH 03/20] [ci] Add cloud image annotation (#120165) * [ci] Add cloud image annotation * typo Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/scripts/build_kibana.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index e811af224e9af5..61a1ba4ee1ce53 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -28,6 +28,11 @@ if [[ "${GITHUB_PR_LABELS:-}" == *"ci:deploy-cloud"* ]]; then --skip-docker-ubi \ --skip-docker-centos \ --skip-docker-contexts + + CLOUD_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" docker.elastic.co/kibana-ci/kibana-cloud) + cat << EOF | buildkite-agent annotate --style "info" --context cloud-image + Cloud image: $CLOUD_IMAGE +EOF fi echo "--- Archive Kibana Distribution" From b1bb4a93959f19a653b9cfb207a5c6acb6559482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Thu, 2 Dec 2021 00:26:48 +0100 Subject: [PATCH 04/20] [APM] Disable telemetry in agent config endpoint (#120106) --- .../apm/server/routes/settings/agent_configuration/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts index 1122c2095ed3de..26a9eaefc04135 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts @@ -186,7 +186,7 @@ const agentConfigurationSearchRoute = createApmServerRoute({ params: t.type({ body: searchParamsRt, }), - options: { tags: ['access:apm'] }, + options: { tags: ['access:apm'], disableTelemetry: true }, handler: async (resources) => { const { params, logger } = resources; From 178e1d6c7d1e5b100b1de4f9abf49a0d638eac9b Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Wed, 1 Dec 2021 17:57:46 -0700 Subject: [PATCH 05/20] [Security Solution] Fixes the formatting of the Alert Summary View in Timeline (#120185) ## [Security Solution] Fixes the formatting of the Alert Summary View in Timeline This PR fixes a formatting issue in Timeline resulting from redundant actions, (see for repro steps), as shown in the Before / After screenshots below: **Before** ![redundant-actions-cause-formatting-issue](https://user-images.githubusercontent.com/4459398/144320934-6e929c83-e096-4b5c-a8ab-36863a8b9ded.png) _Above: Before - The Alert summary view in Timeline renders redundant actions, resulting in the formatting above_ **After** ![redundant-actions-fixed](https://user-images.githubusercontent.com/4459398/144324353-3337700f-11ab-4c48-ae80-b881ab73131a.png) _Above: After - The redundant hover actions are gone, and the formatting is correct_ ### No changes to the `Security > Alerts` view The Security Alerts view is **unchanged**, per the screenshots below: **Before** ![security_alerts_before_fix](https://user-images.githubusercontent.com/4459398/144323960-7b95ab29-60fa-47ab-b258-9dd9ded4efe6.png) _Above: Before - Hover actions in the Alert summary in Security > Alerts before the fix_ **After** ![security_alerts_after_fix_noop](https://user-images.githubusercontent.com/4459398/144324007-09df11b7-d22c-4600-bdc6-b1ff113f34ed.png) _Above: After - `noop` The Alert summary in Security > Alerts remains **unchanged** after the fix_ --- .../event_details/alert_summary_view.test.tsx | 19 ++++++++++++++++ .../event_details/alert_summary_view.tsx | 22 ++++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx index 8f4bed01960719..f7522cb4dd5858 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.test.tsx @@ -14,6 +14,7 @@ import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { useRuleWithFallback } from '../../../detections/containers/detection_engine/rules/use_rule_with_fallback'; import { TestProviders, TestProvidersComponent } from '../../mock'; +import { TimelineId } from '../../../../common'; import { mockBrowserFields } from '../../containers/source/mock'; jest.mock('../../lib/kibana'); @@ -49,6 +50,24 @@ describe('AlertSummaryView', () => { expect(getByTestId('summary-view')).toBeInTheDocument(); }); + test('it renders the action cell by default', () => { + const { getAllByTestId } = render( + + + + ); + expect(getAllByTestId('hover-actions-filter-for').length).toBeGreaterThan(0); + }); + + test('it does NOT render the action cell for the active timeline', () => { + const { queryAllByTestId } = render( + + + + ); + expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0); + }); + test("render no investigation guide if it doesn't exist", async () => { (useRuleWithFallback as jest.Mock).mockReturnValue({ rule: { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 19a23e5002567f..82259aa2312ae6 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -14,7 +14,7 @@ import { AlertSummaryRow, getSummaryColumns, SummaryRow } from './helpers'; import { ActionCell } from './table/action_cell'; import { FieldValueCell } from './table/field_value_cell'; -import { TimelineEventsDetailsItem } from '../../../../common'; +import { TimelineEventsDetailsItem, TimelineId } from '../../../../common'; import { getSummaryRows } from './get_alert_summary_rows'; @@ -37,15 +37,17 @@ const getDescription = ({ isDraggable={isDraggable} values={values} /> - + {timelineId !== TimelineId.active && ( + + )} ); From eb3c4b57a341b12d4bddfcb8483df178d70a0257 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 1 Dec 2021 18:43:56 -0700 Subject: [PATCH 06/20] [Maps] change resolution input from select to range slider (#120016) * [Maps] change resolution input from select to range slider * set title of card to match wizard title * tslint * update snapshots * update snapshots * decrease max icon size * change maxSize in wizard and not default props --- .../resolution_editor.test.tsx.snap | 36 ++++--- .../update_source_editor.test.tsx.snap | 14 +-- .../clusters_layer_wizard.tsx | 7 +- .../heatmap_layer_wizard.tsx | 7 +- .../resolution_editor.test.tsx | 1 + .../es_geo_grid_source/resolution_editor.tsx | 101 +++++++++++------- .../update_source_editor.tsx | 7 +- .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 9 files changed, 106 insertions(+), 77 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap index 75b865a936238e..90a5bd6758bde8 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap @@ -8,33 +8,45 @@ exports[`render 1`] = ` fullWidth={false} hasChildLabel={true} hasEmptyLabelSpace={false} - label="Grid resolution" + label="Resolution" labelType="label" > - diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap index e6c6303642a6b1..7043613b8e20ae 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -34,17 +34,14 @@ exports[`source editor geo_grid_source should not allow editing multiple metrics size="xs" >
- + Heat map
- + Clusters and grids
{}, metrics: [], diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.tsx index 5e554ae6566e17..72dec662791645 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.tsx @@ -5,45 +5,51 @@ * 2.0. */ -import React, { ChangeEvent, Component } from 'react'; -import { EuiConfirmModal, EuiSelect, EuiFormRow } from '@elastic/eui'; +import React, { ChangeEvent, Component, MouseEvent } from 'react'; +import { EuiConfirmModal, EuiFormRow, EuiRange } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { AggDescriptor } from '../../../../common/descriptor_types'; import { AGG_TYPE, GRID_RESOLUTION } from '../../../../common/constants'; -const OPTIONS = [ - { - value: GRID_RESOLUTION.COARSE, - text: i18n.translate('xpack.maps.source.esGrid.coarseDropdownOption', { - defaultMessage: 'coarse', - }), - }, - { - value: GRID_RESOLUTION.FINE, - text: i18n.translate('xpack.maps.source.esGrid.fineDropdownOption', { - defaultMessage: 'fine', - }), - }, - { - value: GRID_RESOLUTION.MOST_FINE, - text: i18n.translate('xpack.maps.source.esGrid.finestDropdownOption', { - defaultMessage: 'finest', - }), - }, - { - value: GRID_RESOLUTION.SUPER_FINE, - text: i18n.translate('xpack.maps.source.esGrid.superFineDropDownOption', { - defaultMessage: 'super fine', - }), - }, -]; +function resolutionToSliderValue(resolution: GRID_RESOLUTION) { + if (resolution === GRID_RESOLUTION.SUPER_FINE) { + return 4; + } + + if (resolution === GRID_RESOLUTION.MOST_FINE) { + return 3; + } + + if (resolution === GRID_RESOLUTION.FINE) { + return 2; + } + + return 1; +} + +function sliderValueToResolution(value: number) { + if (value === 4) { + return GRID_RESOLUTION.SUPER_FINE; + } + + if (value === 3) { + return GRID_RESOLUTION.MOST_FINE; + } + + if (value === 2) { + return GRID_RESOLUTION.FINE; + } + + return GRID_RESOLUTION.COARSE; +} function isUnsupportedVectorTileMetric(metric: AggDescriptor) { return metric.type === AGG_TYPE.TERMS; } interface Props { + isHeatmap: boolean; resolution: GRID_RESOLUTION; onChange: (resolution: GRID_RESOLUTION, metrics: AggDescriptor[]) => void; metrics: AggDescriptor[]; @@ -58,9 +64,9 @@ export class ResolutionEditor extends Component { showModal: false, }; - _onResolutionChange = (e: ChangeEvent) => { - const resolution = e.target.value as GRID_RESOLUTION; - if (resolution === GRID_RESOLUTION.SUPER_FINE) { + _onResolutionChange = (event: ChangeEvent | MouseEvent) => { + const resolution = sliderValueToResolution(parseInt(event.currentTarget.value, 10)); + if (!this.props.isHeatmap && resolution === GRID_RESOLUTION.SUPER_FINE) { const hasUnsupportedMetrics = this.props.metrics.find(isUnsupportedVectorTileMetric); if (hasUnsupportedMetrics) { this.setState({ showModal: true }); @@ -114,7 +120,7 @@ export class ResolutionEditor extends Component {

@@ -123,9 +129,9 @@ export class ResolutionEditor extends Component { render() { const helpText = - this.props.resolution === GRID_RESOLUTION.SUPER_FINE + !this.props.isHeatmap && this.props.resolution === GRID_RESOLUTION.SUPER_FINE ? i18n.translate('xpack.maps.source.esGrid.superFineHelpText', { - defaultMessage: 'Super fine grid resolution uses vector tiles.', + defaultMessage: 'High resolution uses vector tiles.', }) : undefined; return ( @@ -133,15 +139,34 @@ export class ResolutionEditor extends Component { {this._renderModal()} - diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index 60a4c56fdb3b87..ba10479a2bd2cf 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -20,6 +20,7 @@ import { IndexPatternField, indexPatterns } from '../../../../../../../src/plugi import { RenderAsSelect } from './render_as_select'; import { AggDescriptor } from '../../../../common/descriptor_types'; import { OnSourceChangeArgs } from '../source'; +import { clustersTitle, heatmapTitle } from './es_geo_grid_source'; interface Props { currentLayerType?: string; @@ -147,14 +148,12 @@ export class UpdateSourceEditor extends Component {
- + {this.props.currentLayerType === LAYER_TYPE.HEATMAP ? heatmapTitle : clustersTitle}
Date: Thu, 2 Dec 2021 02:34:59 +0000 Subject: [PATCH 07/20] chore(NA): use internal pkg_npm on @kbn/babel-preset (#120123) --- packages/kbn-babel-preset/BUILD.bazel | 7 ++++--- packages/kbn-react-field/BUILD.bazel | 1 - packages/kbn-ui-shared-deps-src/BUILD.bazel | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/kbn-babel-preset/BUILD.bazel b/packages/kbn-babel-preset/BUILD.bazel index ebead1ae0bfbdd..8e600eb6b23be1 100644 --- a/packages/kbn-babel-preset/BUILD.bazel +++ b/packages/kbn-babel-preset/BUILD.bazel @@ -1,4 +1,5 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "pkg_npm") PKG_BASE_NAME = "kbn-babel-preset" PKG_REQUIRE_NAME = "@kbn/babel-preset" @@ -24,7 +25,7 @@ NPM_MODULE_EXTRA_FILES = [ "README.md", ] -DEPS = [ +RUNTIME_DEPS = [ "@npm//@babel/plugin-proposal-class-properties", "@npm//@babel/plugin-proposal-export-namespace-from", "@npm//@babel/plugin-proposal-nullish-coalescing-operator", @@ -46,7 +47,7 @@ js_library( srcs = NPM_MODULE_EXTRA_FILES + [ ":srcs", ], - deps = DEPS, + deps = RUNTIME_DEPS, package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-react-field/BUILD.bazel b/packages/kbn-react-field/BUILD.bazel index 9cb2df76bd6c94..d7645d86dbd1a7 100644 --- a/packages/kbn-react-field/BUILD.bazel +++ b/packages/kbn-react-field/BUILD.bazel @@ -42,7 +42,6 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/kbn-babel-preset", "//packages/kbn-i18n", "@npm//tslib", "@npm//@types/jest", diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index 3da5e0ed9a6ffd..454121392ac68b 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -45,7 +45,6 @@ TYPES_DEPS = [ "//packages/elastic-datemath:npm_module_types", "//packages/elastic-safer-lodash-set", "//packages/kbn-analytics:npm_module_types", - "//packages/kbn-babel-preset", "//packages/kbn-i18n:npm_module_types", "//packages/kbn-i18n-react:npm_module_types", "//packages/kbn-monaco", From 54c4df07ade215a892cfa60c19b57df1fd4709cc Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Thu, 2 Dec 2021 09:31:24 +0100 Subject: [PATCH 08/20] [Console] Unskip api integration proxy test (#120075) * Unskip test * commit using @elastic.co Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/api_integration/apis/console/proxy_route.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/api_integration/apis/console/proxy_route.ts b/test/api_integration/apis/console/proxy_route.ts index a208ef405306ff..d8a5f57a41a6e3 100644 --- a/test/api_integration/apis/console/proxy_route.ts +++ b/test/api_integration/apis/console/proxy_route.ts @@ -12,8 +12,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - // Failing: See https://github.com/elastic/kibana/issues/117674 - describe.skip('POST /api/console/proxy', () => { + describe('POST /api/console/proxy', () => { describe('system indices behavior', () => { it('returns warning header when making requests to .kibana index', async () => { return await supertest From 3cd176fffbf5c0a34de73eb2cd8794f69c8b0620 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 2 Dec 2021 12:06:29 +0300 Subject: [PATCH 09/20] [Lens] Waffle visualization type (#119339) * [WIP][Lens] Waffle visualization type Closes: #107059 * add showExtraLegend for waffle * add tests * resolved 1 and 5 * resolved 6 * add sortPredicate for waffle chart type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/expressions/pie_chart/types.ts | 2 +- .../lens/public/assets/chart_waffle.tsx | 31 +++ .../public/pie_visualization/constants.ts | 97 -------- .../partition_charts_meta.ts | 214 ++++++++++++++++++ .../pie_visualization/render_function.tsx | 28 +-- .../pie_visualization/render_helpers.test.ts | 57 +++++ .../pie_visualization/render_helpers.ts | 14 +- .../pie_visualization/suggestions.test.ts | 133 ++++++++++- .../public/pie_visualization/suggestions.ts | 99 ++++++-- .../public/pie_visualization/to_expression.ts | 6 +- .../lens/public/pie_visualization/toolbar.tsx | 85 +++---- .../pie_visualization/visualization.tsx | 114 +++++----- 12 files changed, 641 insertions(+), 239 deletions(-) create mode 100644 x-pack/plugins/lens/public/assets/chart_waffle.tsx create mode 100644 x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts index 00fc7abaa043bc..8c9ec4e5a54e77 100644 --- a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts +++ b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts @@ -8,7 +8,7 @@ import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; import type { LensMultiTable, LayerType } from '../../types'; -export type PieChartTypes = 'donut' | 'pie' | 'treemap' | 'mosaic'; +export type PieChartTypes = 'donut' | 'pie' | 'treemap' | 'mosaic' | 'waffle'; export interface SharedPieLayerState { groups: string[]; diff --git a/x-pack/plugins/lens/public/assets/chart_waffle.tsx b/x-pack/plugins/lens/public/assets/chart_waffle.tsx new file mode 100644 index 00000000000000..b9ee0557faea9f --- /dev/null +++ b/x-pack/plugins/lens/public/assets/chart_waffle.tsx @@ -0,0 +1,31 @@ +/* + * 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 React from 'react'; +import type { EuiIconProps } from '@elastic/eui'; + +export const LensIconChartWaffle = ({ title, titleId, ...props }: Omit) => ( + + {title ? : null} + + + +); diff --git a/x-pack/plugins/lens/public/pie_visualization/constants.ts b/x-pack/plugins/lens/public/pie_visualization/constants.ts index be0afc65aed3bc..bfb263b4158914 100644 --- a/x-pack/plugins/lens/public/pie_visualization/constants.ts +++ b/x-pack/plugins/lens/public/pie_visualization/constants.ts @@ -5,101 +5,4 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import { PartitionLayout } from '@elastic/charts'; -import { LensIconChartDonut } from '../assets/chart_donut'; -import { LensIconChartPie } from '../assets/chart_pie'; -import { LensIconChartTreemap } from '../assets/chart_treemap'; -import { LensIconChartMosaic } from '../assets/chart_mosaic'; - -import type { SharedPieLayerState } from '../../common/expressions'; - -interface CategoryOption { - value: SharedPieLayerState['categoryDisplay']; - inputDisplay: string; -} - -const groupLabel = i18n.translate('xpack.lens.pie.groupLabel', { - defaultMessage: 'Proportion', -}); - -const categoryOptions: CategoryOption[] = [ - { - value: 'default', - inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', { - defaultMessage: 'Inside or outside', - }), - }, - { - value: 'inside', - inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', { - defaultMessage: 'Inside only', - }), - }, - { - value: 'hide', - inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { - defaultMessage: 'Hide labels', - }), - }, -]; - -const categoryOptionsTreemap: CategoryOption[] = [ - { - value: 'default', - inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', { - defaultMessage: 'Show labels', - }), - }, - { - value: 'hide', - inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { - defaultMessage: 'Hide labels', - }), - }, -]; - -export const CHART_NAMES = { - donut: { - icon: LensIconChartDonut, - label: i18n.translate('xpack.lens.pie.donutLabel', { - defaultMessage: 'Donut', - }), - partitionType: PartitionLayout.sunburst, - groupLabel, - categoryOptions, - }, - pie: { - icon: LensIconChartPie, - label: i18n.translate('xpack.lens.pie.pielabel', { - defaultMessage: 'Pie', - }), - partitionType: PartitionLayout.sunburst, - groupLabel, - categoryOptions, - }, - treemap: { - icon: LensIconChartTreemap, - label: i18n.translate('xpack.lens.pie.treemaplabel', { - defaultMessage: 'Treemap', - }), - partitionType: PartitionLayout.treemap, - groupLabel, - categoryOptions: categoryOptionsTreemap, - }, - mosaic: { - icon: LensIconChartMosaic, - label: i18n.translate('xpack.lens.pie.mosaiclabel', { - defaultMessage: 'Mosaic', - }), - partitionType: PartitionLayout.mosaic, - groupLabel, - categoryOptions: [] as CategoryOption[], - }, -}; - -export const MAX_PIE_BUCKETS = 3; -export const MAX_TREEMAP_BUCKETS = 2; -export const MAX_MOSAIC_BUCKETS = 2; - export const DEFAULT_PERCENT_DECIMALS = 2; diff --git a/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts b/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts new file mode 100644 index 00000000000000..4f16ab01ba19cf --- /dev/null +++ b/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts @@ -0,0 +1,214 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ArrayEntry, PartitionLayout } from '@elastic/charts'; +import type { EuiIconProps } from '@elastic/eui'; + +import { LensIconChartDonut } from '../assets/chart_donut'; +import { LensIconChartPie } from '../assets/chart_pie'; +import { LensIconChartTreemap } from '../assets/chart_treemap'; +import { LensIconChartMosaic } from '../assets/chart_mosaic'; +import { LensIconChartWaffle } from '../assets/chart_waffle'; + +import type { SharedPieLayerState } from '../../common/expressions'; +import type { PieChartTypes } from '../../common/expressions/pie_chart/types'; +import type { DatatableColumn } from '../../../../../src/plugins/expressions'; + +interface PartitionChartMeta { + icon: ({ title, titleId, ...props }: Omit) => JSX.Element; + label: string; + partitionType: PartitionLayout; + groupLabel: string; + maxBuckets: number; + isExperimental?: boolean; + requiredMinDimensionCount?: number; + toolbarPopover: { + isDisabled?: boolean; + categoryOptions: Array<{ + value: SharedPieLayerState['categoryDisplay']; + inputDisplay: string; + }>; + numberOptions: Array<{ + value: SharedPieLayerState['numberDisplay']; + inputDisplay: string; + }>; + }; + legend: { + flat?: boolean; + showValues?: boolean; + getShowLegendDefault?: (bucketColumns: DatatableColumn[]) => boolean; + }; + sortPredicate?: ( + bucketColumns: DatatableColumn[], + sortingMap: Record + ) => (node1: ArrayEntry, node2: ArrayEntry) => number; +} + +const groupLabel = i18n.translate('xpack.lens.pie.groupLabel', { + defaultMessage: 'Proportion', +}); + +const categoryOptions: PartitionChartMeta['toolbarPopover']['categoryOptions'] = [ + { + value: 'default', + inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', { + defaultMessage: 'Inside or outside', + }), + }, + { + value: 'inside', + inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', { + defaultMessage: 'Inside only', + }), + }, + { + value: 'hide', + inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { + defaultMessage: 'Hide labels', + }), + }, +]; + +const categoryOptionsTreemap: PartitionChartMeta['toolbarPopover']['categoryOptions'] = [ + { + value: 'default', + inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', { + defaultMessage: 'Show labels', + }), + }, + { + value: 'hide', + inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', { + defaultMessage: 'Hide labels', + }), + }, +]; + +const numberOptions: PartitionChartMeta['toolbarPopover']['numberOptions'] = [ + { + value: 'hidden', + inputDisplay: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', { + defaultMessage: 'Hide from chart', + }), + }, + { + value: 'percent', + inputDisplay: i18n.translate('xpack.lens.pieChart.showPercentValuesLabel', { + defaultMessage: 'Show percent', + }), + }, + { + value: 'value', + inputDisplay: i18n.translate('xpack.lens.pieChart.showFormatterValuesLabel', { + defaultMessage: 'Show value', + }), + }, +]; + +export const PartitionChartsMeta: Record = { + donut: { + icon: LensIconChartDonut, + label: i18n.translate('xpack.lens.pie.donutLabel', { + defaultMessage: 'Donut', + }), + partitionType: PartitionLayout.sunburst, + groupLabel, + maxBuckets: 3, + toolbarPopover: { + categoryOptions, + numberOptions, + }, + legend: { + getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1, + }, + }, + pie: { + icon: LensIconChartPie, + label: i18n.translate('xpack.lens.pie.pielabel', { + defaultMessage: 'Pie', + }), + partitionType: PartitionLayout.sunburst, + groupLabel, + maxBuckets: 3, + toolbarPopover: { + categoryOptions, + numberOptions, + }, + legend: { + getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1, + }, + }, + treemap: { + icon: LensIconChartTreemap, + label: i18n.translate('xpack.lens.pie.treemaplabel', { + defaultMessage: 'Treemap', + }), + partitionType: PartitionLayout.treemap, + groupLabel, + maxBuckets: 2, + toolbarPopover: { + categoryOptions: categoryOptionsTreemap, + numberOptions, + }, + legend: { + getShowLegendDefault: () => false, + }, + }, + mosaic: { + icon: LensIconChartMosaic, + label: i18n.translate('xpack.lens.pie.mosaiclabel', { + defaultMessage: 'Mosaic', + }), + partitionType: PartitionLayout.mosaic, + groupLabel, + maxBuckets: 2, + isExperimental: true, + toolbarPopover: { + categoryOptions: [], + numberOptions, + }, + legend: { + getShowLegendDefault: () => false, + }, + requiredMinDimensionCount: 2, + sortPredicate: + (bucketColumns, sortingMap) => + ([name1, node1], [, node2]) => { + // Sorting for first group + if (bucketColumns.length === 1 || (node1.children.length && name1 in sortingMap)) { + return sortingMap[name1]; + } + // Sorting for second group + return node2.value - node1.value; + }, + }, + waffle: { + icon: LensIconChartWaffle, + label: i18n.translate('xpack.lens.pie.wafflelabel', { + defaultMessage: 'Waffle', + }), + partitionType: PartitionLayout.waffle, + groupLabel, + maxBuckets: 1, + isExperimental: true, + toolbarPopover: { + isDisabled: true, + categoryOptions: [], + numberOptions: [], + }, + legend: { + flat: true, + showValues: true, + getShowLegendDefault: () => true, + }, + sortPredicate: + () => + ([, node1], [, node2]) => + node2.value - node1.value, + }, +}; diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 7bc5f4037c3e86..539d69207f5f92 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -25,7 +25,8 @@ import { import { RenderMode } from 'src/plugins/expressions'; import type { LensFilterEvent } from '../types'; import { VisualizationContainer } from '../visualization_container'; -import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants'; +import { DEFAULT_PERCENT_DECIMALS } from './constants'; +import { PartitionChartsMeta } from './partition_charts_meta'; import type { FormatFactory } from '../../common'; import type { PieExpressionProps } from '../../common/expressions'; import { @@ -126,7 +127,7 @@ export function PieComponent( ); } - let sortingMap: Record; + let sortingMap: Record = {}; if (shape === 'mosaic') { sortingMap = extractUniqTermsMap(firstTable, bucketColumns[0].id); } @@ -145,17 +146,7 @@ export function PieComponent( return String(d); }, fillLabel, - sortPredicate: - shape === 'mosaic' - ? ([name1, node1], [, node2]) => { - // Sorting for first group - if (bucketColumns.length === 1 || (node1.children.length && name1 in sortingMap)) { - return sortingMap[name1]; - } - // Sorting for second group - return node2.value - node1.value; - } - : undefined, + sortPredicate: PartitionChartsMeta[shape].sortPredicate?.(bucketColumns, sortingMap), shape: { fillColor: (d) => { const seriesLayers: SeriesLayer[] = []; @@ -209,8 +200,10 @@ export function PieComponent( }; }); + const { legend, partitionType: partitionLayout, label: chartType } = PartitionChartsMeta[shape]; + const config: RecursivePartial = { - partitionLayout: CHART_NAMES[shape].partitionType, + partitionLayout, fontFamily: chartTheme.barSeriesStyle?.displayValue?.fontFamily, outerSizeRatio: 1, specialFirstInnermostSector: true, @@ -292,7 +285,7 @@ export function PieComponent( id="xpack.lens.pie.pieWithNegativeWarningLabel" defaultMessage="{chartType} charts can't render with negative values." values={{ - chartType: CHART_NAMES[shape].label, + chartType, }} /> @@ -319,9 +312,10 @@ export function PieComponent( !hideLabels && (legendDisplay === 'show' || (legendDisplay === 'default' && - bucketColumns.length > 1 && - !isTreemapOrMosaicShape(shape))) + (legend.getShowLegendDefault?.(bucketColumns) ?? false))) } + flatLegend={legend.flat} + showLegendExtra={legend.showValues} legendPosition={legendPosition || Position.Right} legendMaxDepth={nestedLegend ? undefined : 1 /* Color is based only on first layer */} onElementClick={props.interactive ?? true ? onElementClickHandler : undefined} diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts index dd27632b36e443..d86500ff8a4fad 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts @@ -13,6 +13,7 @@ import { getFilterContext, byDataColorPaletteMap, extractUniqTermsMap, + checkTableForContainsSmallValues, } from './render_helpers'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; @@ -317,4 +318,60 @@ describe('render helpers', () => { ); }); }); + + describe('#checkTableForContainsSmallValues', () => { + let datatable: Datatable; + const columnId = 'foo'; + + beforeEach(() => { + datatable = { + rows: [], + } as unknown as Datatable; + }); + + it('should return true if the data contains values less than the target percentage (1)', () => { + datatable.rows = [ + { + [columnId]: 80, + }, + { + [columnId]: 20, + }, + { + [columnId]: 1, + }, + ]; + expect(checkTableForContainsSmallValues(datatable, columnId, 1)).toBeTruthy(); + }); + + it('should return true if the data contains values less than the target percentage (42)', () => { + datatable.rows = [ + { + [columnId]: 58, + }, + { + [columnId]: 42, + }, + { + [columnId]: 1, + }, + ]; + expect(checkTableForContainsSmallValues(datatable, columnId, 42)).toBeTruthy(); + }); + + it('should return false if the data contains values greater than the target percentage', () => { + datatable.rows = [ + { + [columnId]: 22, + }, + { + [columnId]: 56, + }, + { + [columnId]: 12, + }, + ]; + expect(checkTableForContainsSmallValues(datatable, columnId, 1)).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts index bdffacde656391..fa20eb6f20fa8d 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts @@ -39,7 +39,7 @@ export function getFilterContext( } export const isPartitionShape = (shape: PieChartTypes | string) => - ['donut', 'pie', 'treemap', 'mosaic'].includes(shape); + ['donut', 'pie', 'treemap', 'mosaic', 'waffle'].includes(shape); export const isTreemapOrMosaicShape = (shape: PieChartTypes | string) => ['treemap', 'mosaic'].includes(shape); @@ -95,3 +95,15 @@ export const byDataColorPaletteMap = ( }, }; }; + +export const checkTableForContainsSmallValues = ( + dataTable: Datatable, + columnId: string, + minPercentage: number +) => { + const overallSum = dataTable.rows.reduce( + (partialSum, row) => Number(row[columnId]) + partialSum, + 0 + ); + return dataTable.rows.some((row) => (row[columnId] / overallSum) * 100 < minPercentage); +}; diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts index 656d00960766e2..92dde282da5022 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts @@ -304,7 +304,7 @@ describe('suggestions', () => { state: undefined, keptLayerIds: ['first'], }); - expect(currentSuggestions).toHaveLength(4); + expect(currentSuggestions).toHaveLength(5); expect(currentSuggestions.every((s) => s.hide)).toEqual(true); }); @@ -324,7 +324,7 @@ describe('suggestions', () => { state: undefined, keptLayerIds: ['first'], }); - expect(currentSuggestions).toHaveLength(4); + expect(currentSuggestions).toHaveLength(5); expect(currentSuggestions.every((s) => s.hide)).toEqual(true); }); @@ -921,4 +921,133 @@ describe('suggestions', () => { ).toMatchInlineSnapshot(`Array []`); }); }); + + describe('waffle', () => { + it('should reject when currently active and unchanged data', () => { + expect( + suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [], + changeType: 'unchanged', + }, + state: { + shape: 'waffle', + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + groups: [], + metric: 'a', + + numberDisplay: 'hidden', + categoryDisplay: 'default', + legendDisplay: 'default', + }, + ], + }, + keptLayerIds: ['first'], + }) + ).toHaveLength(0); + }); + + it('waffle type should be added only in case of 1 group', () => { + expect( + suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'unchanged', + }, + state: { + shape: 'waffle', + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + groups: ['a', 'b'], + metric: 'c', + + numberDisplay: 'hidden', + categoryDisplay: 'inside', + legendDisplay: 'show', + percentDecimals: 0, + legendMaxLines: 1, + truncateLegend: true, + nestedLegend: true, + }, + ], + }, + keptLayerIds: ['first'], + }).filter(({ hide, state }) => !hide && state.shape === 'waffle') + ).toMatchInlineSnapshot(`Array []`); + }); + + it('waffle type should be added only in case of 1 group (negative test)', () => { + const meta: Parameters[0] = { + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'c', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'unchanged', + }, + state: { + shape: 'pie', + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + groups: ['a', 'b'], + metric: 'c', + + numberDisplay: 'hidden', + categoryDisplay: 'inside', + legendDisplay: 'show', + percentDecimals: 0, + legendMaxLines: 1, + truncateLegend: true, + nestedLegend: true, + }, + ], + }, + keptLayerIds: ['first'], + }; + + // test with no group + expect( + suggestions(meta).filter(({ hide, state }) => !hide && state.shape === 'waffle') + ).toMatchInlineSnapshot(`Array []`); + + meta.table.columns.push({ + columnId: 'b', + operation: { label: 'Top 6', dataType: 'string' as DataType, isBucketed: true }, + }); + + meta.table.columns.push({ + columnId: 'c', + operation: { label: 'Top 7', dataType: 'string' as DataType, isBucketed: true }, + }); + + // test with 2 groups + expect( + suggestions(meta).filter(({ hide, state }) => !hide && state.shape === 'waffle') + ).toMatchInlineSnapshot(`Array []`); + }); + }); }); diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts index 30cd63752f4200..f638bfd908be4b 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import type { SuggestionRequest, TableSuggestionColumn, VisualizationSuggestion } from '../types'; import { layerTypes } from '../../common'; import type { PieVisualizationState } from '../../common/expressions'; -import { CHART_NAMES, MAX_MOSAIC_BUCKETS, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants'; -import { isPartitionShape, isTreemapOrMosaicShape } from './render_helpers'; +import { PartitionChartsMeta } from './partition_charts_meta'; +import { isPartitionShape } from './render_helpers'; +import { PieChartTypes } from '../../common/expressions/pie_chart/types'; function hasIntervalScale(columns: TableSuggestionColumn[]) { return columns.some((col) => col.operation.scale === 'interval'); @@ -30,6 +31,31 @@ function shouldReject({ table, keptLayerIds, state }: SuggestionRequest maxBuckets) +); + export function suggestions({ table, state, @@ -45,7 +71,7 @@ export function suggestions({ const [groups, metrics] = partition(table.columns, (col) => col.operation.isBucketed); - if (metrics.length > 1 || groups.length > Math.max(MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS)) { + if (metrics.length > 1 || groups.length > maximumGroupLength) { return []; } @@ -61,20 +87,18 @@ export function suggestions({ const results: Array> = []; - if (groups.length <= MAX_PIE_BUCKETS && !isTreemapOrMosaicShape(subVisualizationId!)) { - let newShape: PieVisualizationState['shape'] = - (subVisualizationId as PieVisualizationState['shape']) || 'donut'; - if (groups.length !== 1 && !subVisualizationId) { - newShape = 'pie'; - } - + if ( + groups.length <= PartitionChartsMeta.pie.maxBuckets && + !hasCustomSuggestionsExists(subVisualizationId) + ) { + const newShape = getNewShape(groups, subVisualizationId as PieVisualizationState['shape']); const baseSuggestion: VisualizationSuggestion = { title: i18n.translate('xpack.lens.pie.suggestionLabel', { defaultMessage: 'As {chartName}', - values: { chartName: CHART_NAMES[newShape].label }, + values: { chartName: PartitionChartsMeta[newShape].label }, description: 'chartName is already translated', }), - score: state && !isTreemapOrMosaicShape(state.shape) ? 0.6 : 0.4, + score: state && !hasCustomSuggestionsExists(state.shape) ? 0.6 : 0.4, state: { shape: newShape, palette: mainPalette || state?.palette, @@ -104,7 +128,7 @@ export function suggestions({ hide: table.changeType === 'reduced' || hasIntervalScale(groups) || - (state && !isTreemapOrMosaicShape(state.shape)), + (state && !hasCustomSuggestionsExists(state.shape)), }; results.push(baseSuggestion); @@ -112,7 +136,7 @@ export function suggestions({ ...baseSuggestion, title: i18n.translate('xpack.lens.pie.suggestionLabel', { defaultMessage: 'As {chartName}', - values: { chartName: CHART_NAMES[newShape === 'pie' ? 'donut' : 'pie'].label }, + values: { chartName: PartitionChartsMeta[newShape === 'pie' ? 'donut' : 'pie'].label }, description: 'chartName is already translated', }), score: 0.1, @@ -125,7 +149,7 @@ export function suggestions({ } if ( - groups.length <= MAX_TREEMAP_BUCKETS && + groups.length <= PartitionChartsMeta.treemap.maxBuckets && (!subVisualizationId || subVisualizationId === 'treemap') ) { results.push({ @@ -174,7 +198,7 @@ export function suggestions({ } if ( - groups.length <= MAX_MOSAIC_BUCKETS && + groups.length <= PartitionChartsMeta.mosaic.maxBuckets && (!subVisualizationId || subVisualizationId === 'mosaic') ) { results.push({ @@ -216,6 +240,49 @@ export function suggestions({ }); } + if ( + groups.length <= PartitionChartsMeta.waffle.maxBuckets && + (!subVisualizationId || subVisualizationId === 'waffle') + ) { + results.push({ + title: i18n.translate('xpack.lens.pie.waffleSuggestionLabel', { + defaultMessage: 'As Waffle', + }), + score: state?.shape === 'waffle' ? 0.7 : 0.5, + state: { + shape: 'waffle', + palette: mainPalette || state?.palette, + layers: [ + state?.layers[0] + ? { + ...state.layers[0], + layerId: table.layerId, + groups: groups.map((col) => col.columnId), + metric: metricColumnId, + categoryDisplay: 'default', + layerType: layerTypes.DATA, + } + : { + layerId: table.layerId, + groups: groups.map((col) => col.columnId), + metric: metricColumnId, + numberDisplay: 'percent', + categoryDisplay: 'default', + legendDisplay: 'default', + nestedLegend: false, + layerType: layerTypes.DATA, + }, + ], + }, + previewIcon: 'bullseye', + hide: + groups.length !== 1 || + table.changeType === 'reduced' || + hasIntervalScale(groups) || + (state && state.shape === 'waffle'), + }); + } + return [...results] .map((suggestion) => ({ ...suggestion, diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index fd754906ceb021..e13fbf62708eeb 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -55,7 +55,11 @@ function expressionHelper( categoryDisplay: [layer.categoryDisplay], legendDisplay: [layer.legendDisplay], legendPosition: [layer.legendPosition || 'right'], - percentDecimals: [layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS], + percentDecimals: [ + state.shape === 'waffle' + ? DEFAULT_PERCENT_DECIMALS + : layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS, + ], legendMaxLines: [layer.legendMaxLines ?? 1], truncateLegend: [layer.truncateLegend ?? true], nestedLegend: [!!layer.nestedLegend], diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index 23003a4ec34043..195a72cca9fedd 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -17,36 +17,13 @@ import { } from '@elastic/eui'; import type { Position } from '@elastic/charts'; import type { PaletteRegistry } from 'src/plugins/charts/public'; -import { DEFAULT_PERCENT_DECIMALS, CHART_NAMES } from './constants'; +import { DEFAULT_PERCENT_DECIMALS } from './constants'; +import { PartitionChartsMeta } from './partition_charts_meta'; import type { PieVisualizationState, SharedPieLayerState } from '../../common/expressions'; import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types'; import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue } from '../shared_components'; import { PalettePicker } from '../shared_components'; -const numberOptions: Array<{ - value: SharedPieLayerState['numberDisplay']; - inputDisplay: string; -}> = [ - { - value: 'hidden', - inputDisplay: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', { - defaultMessage: 'Hide from chart', - }), - }, - { - value: 'percent', - inputDisplay: i18n.translate('xpack.lens.pieChart.showPercentValuesLabel', { - defaultMessage: 'Show percent', - }), - }, - { - value: 'value', - inputDisplay: i18n.translate('xpack.lens.pieChart.showFormatterValuesLabel', { - defaultMessage: 'Show value', - }), - }, -]; - const legendOptions: Array<{ value: SharedPieLayerState['legendDisplay']; label: string; @@ -81,17 +58,24 @@ export function PieToolbar(props: VisualizationToolbarProps - {state.shape && CHART_NAMES[state.shape].categoryOptions.length ? ( + {categoryOptions.length ? ( { setState({ ...state, @@ -112,27 +96,32 @@ export function PieToolbar(props: VisualizationToolbarProps ) : null} - - { - setState({ - ...state, - layers: [{ ...layer, numberDisplay: option }], - }); - }} - /> - - + + {numberOptions.length ? ( + + { + setState({ + ...state, + layers: [{ ...layer, numberDisplay: option }], + }); + }} + /> + + ) : null} + + {numberOptions.length + categoryOptions.length ? : null} + => ({ id: 'lnsPie', - visualizationTypes: [ - { - id: 'donut', - icon: CHART_NAMES.donut.icon, - label: CHART_NAMES.donut.label, - groupLabel: CHART_NAMES.donut.groupLabel, - }, - { - id: 'pie', - icon: CHART_NAMES.pie.icon, - label: CHART_NAMES.pie.label, - groupLabel: CHART_NAMES.pie.groupLabel, - }, - { - id: 'treemap', - icon: CHART_NAMES.treemap.icon, - label: CHART_NAMES.treemap.label, - groupLabel: CHART_NAMES.treemap.groupLabel, - }, - { - id: 'mosaic', - icon: CHART_NAMES.mosaic.icon, - label: CHART_NAMES.mosaic.label, - showExperimentalBadge: true, - groupLabel: CHART_NAMES.mosaic.groupLabel, - }, - ], + visualizationTypes: Object.entries(PartitionChartsMeta).map(([key, meta]) => ({ + id: key, + icon: meta.icon, + label: meta.label, + groupLabel: meta.groupLabel, + showExperimentalBadge: meta.isExperimental, + })), getVisualizationTypeId(state) { return state.shape; @@ -113,7 +94,7 @@ export const getPieVisualization = ({ }, getDescription(state) { - return CHART_NAMES[state.shape] ?? CHART_NAMES.pie; + return PartitionChartsMeta[state.shape] ?? PartitionChartsMeta.pie; }, switchVisualizationType: (visualizationTypeId, state) => ({ @@ -165,25 +146,25 @@ export const getPieVisualization = ({ }; switch (state.shape) { - case 'mosaic': - case 'treemap': + case 'donut': + case 'pie': return { ...baseProps, - groupLabel: i18n.translate('xpack.lens.pie.treemapGroupLabel', { - defaultMessage: 'Group by', + groupLabel: i18n.translate('xpack.lens.pie.sliceGroupLabel', { + defaultMessage: 'Slice by', }), - supportsMoreColumns: sortedColumns.length < MAX_TREEMAP_BUCKETS, - dataTestSubj: 'lnsPie_groupByDimensionPanel', - requiredMinDimensionCount: state.shape === 'mosaic' ? 2 : undefined, + supportsMoreColumns: sortedColumns.length < PartitionChartsMeta.pie.maxBuckets, + dataTestSubj: 'lnsPie_sliceByDimensionPanel', }; default: return { ...baseProps, - groupLabel: i18n.translate('xpack.lens.pie.sliceGroupLabel', { - defaultMessage: 'Slice by', + groupLabel: i18n.translate('xpack.lens.pie.treemapGroupLabel', { + defaultMessage: 'Group by', }), - supportsMoreColumns: sortedColumns.length < MAX_PIE_BUCKETS, - dataTestSubj: 'lnsPie_sliceByDimensionPanel', + supportsMoreColumns: sortedColumns.length < PartitionChartsMeta[state.shape].maxBuckets, + dataTestSubj: 'lnsPie_groupByDimensionPanel', + requiredMinDimensionCount: PartitionChartsMeta[state.shape].requiredMinDimensionCount, }; } }; @@ -279,33 +260,54 @@ export const getPieVisualization = ({ if (state?.layers.length === 0 || !frame.activeData) { return; } - - const metricColumnsWithArrayValues = []; + const warningMessages = []; for (const layer of state.layers) { const { layerId, metric } = layer; - const rows = frame.activeData[layerId] && frame.activeData[layerId].rows; + const rows = frame.activeData[layerId]?.rows; + const numericColumn = frame.activeData[layerId]?.columns.find( + ({ meta }) => meta?.type === 'number' + ); + if (!rows || !metric) { break; } - const columnToLabel = frame.datasourceLayers[layerId].getOperationForColumnId(metric)?.label; + if ( + numericColumn && + state.shape === 'waffle' && + layer.groups.length && + checkTableForContainsSmallValues(frame.activeData[layerId], numericColumn.id, 1) + ) { + warningMessages.push( + {state.shape}, + }} + /> + ); + } + + const columnToLabel = frame.datasourceLayers[layerId].getOperationForColumnId(metric)?.label; const hasArrayValues = rows.some((row) => Array.isArray(row[metric])); if (hasArrayValues) { - metricColumnsWithArrayValues.push(columnToLabel || metric); + warningMessages.push( + {columnToLabel || metric}, + }} + /> + ); } } - return metricColumnsWithArrayValues.map((label) => ( - {label}, - }} - /> - )); + + return warningMessages; }, getErrorMessages(state) { From 3232689b5dfdd248666e73069e1f6bddcce4c846 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 2 Dec 2021 10:47:52 +0100 Subject: [PATCH 10/20] [Uptimes] Push configs to service (#120069) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/uptime/common/types/index.ts | 5 +- .../lib/adapters/framework/adapter_types.ts | 2 + .../synthetics_service/get_api_key.test.ts | 2 +- .../lib/synthetics_service/get_api_key.ts | 13 +- .../get_service_locations.test.ts | 9 +- .../get_service_locations.ts | 7 +- .../synthetics_service/service_api_client.ts | 111 ++++++++++++++++++ .../synthetics_service/synthetics_service.ts | 98 ++++++++++++---- x-pack/plugins/uptime/server/plugin.ts | 17 ++- .../synthetics_service/add_monitor.ts | 16 ++- .../synthetics_service/delete_monitor.ts | 18 ++- .../synthetics_service/edit_monitor.ts | 17 ++- .../get_service_locations.ts | 3 +- x-pack/test/api_integration/config.ts | 2 +- 14 files changed, 269 insertions(+), 51 deletions(-) create mode 100644 x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts diff --git a/x-pack/plugins/uptime/common/types/index.ts b/x-pack/plugins/uptime/common/types/index.ts index e013fb11c2d685..f4218d53c27de7 100644 --- a/x-pack/plugins/uptime/common/types/index.ts +++ b/x-pack/plugins/uptime/common/types/index.ts @@ -33,9 +33,10 @@ export interface MonitorIdParam { export type SyntheticsMonitorSavedObject = SimpleSavedObject<{ name: string; - runOnce: boolean; + runOnce?: boolean; urls?: string[]; tags?: string[]; + locations: string[]; schedule: string; type: 'http' | 'tcp' | 'icmp' | 'browser'; source?: { @@ -59,4 +60,4 @@ export interface ManifestLocation { status: string; } -export type ServiceLocations = Array<{ id: string; label: string; geo: LocationGeo }>; +export type ServiceLocations = Array<{ id: string; label: string; geo: LocationGeo; url: string }>; diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index 2958e3e1719485..c5779a56bfe828 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -26,6 +26,7 @@ import { SecurityPluginStart } from '../../../../../security/server'; import { CloudSetup } from '../../../../../cloud/server'; import { FleetStartContract } from '../../../../../fleet/server'; import { UptimeConfig } from '../../../../common/config'; +import { SyntheticsService } from '../../synthetics_service/synthetics_service'; export type UMElasticsearchQueryFn = ( params: { @@ -47,6 +48,7 @@ export interface UptimeServerSetup { security: SecurityPluginStart; savedObjectsClient: SavedObjectsClientContract; encryptedSavedObjects: EncryptedSavedObjectsPluginStart; + syntheticsService: SyntheticsService; } export interface UptimeCorePluginsSetup { diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts index f9ba0ce545bad0..540dfe150d0370 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts @@ -50,7 +50,7 @@ describe('getAPIKeyTest', function () { cluster: ['monitor', 'read_ilm', 'read_pipeline'], index: [ { - names: ['synthetics-*'], + names: ['synthetics-*', 'heartbeat-*'], privileges: ['view_index_metadata', 'create_doc', 'auto_configure'], }, ], diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts index 015442dd3c4fe8..9ca854598e7868 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts @@ -28,10 +28,15 @@ export const getAPIKeyForSyntheticsService = async ({ includedHiddenTypes: [syntheticsServiceApiKey.name], }); - const apiKey = await getSyntheticsServiceAPIKey(encryptedClient); - if (apiKey) { - return apiKey; + try { + const apiKey = await getSyntheticsServiceAPIKey(encryptedClient); + if (apiKey) { + return apiKey; + } + } catch (err) { + // TODO: figure out how to handle decryption errors } + return await generateAndSaveAPIKey({ request, security, savedObjectsClient }); }; @@ -61,7 +66,7 @@ export const generateAndSaveAPIKey = async ({ cluster: ['monitor', 'read_ilm', 'read_pipeline'], index: [ { - names: ['synthetics-*'], + names: ['synthetics-*', 'heartbeat-*'], privileges: ['view_index_metadata', 'create_doc', 'auto_configure'], }, ], diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.test.ts index 375ceffe492daf..7c4b0d52e5182c 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.test.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.test.ts @@ -27,13 +27,7 @@ describe('getServiceLocations', function () { }); it('should return parsed locations', async () => { const locations = await getServiceLocations({ - config: { - unsafe: { - service: { - manifestUrl: 'http://local.dev', - }, - }, - }, + manifestUrl: 'http://local.dev', }); expect(locations).toEqual([ @@ -44,6 +38,7 @@ describe('getServiceLocations', function () { }, id: 'us_central', label: 'US Central', + url: 'https://local.dev', }, ]); }); diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts index fdd24ed2394b2a..f63a2c7babb97a 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts @@ -6,20 +6,19 @@ */ import axios from 'axios'; -import { UptimeConfig } from '../../../common/config'; import { ManifestLocation, ServiceLocations } from '../../../common/types'; -export async function getServiceLocations({ config }: { config: UptimeConfig }) { - const manifestURL = config.unsafe.service.manifestUrl; +export async function getServiceLocations({ manifestUrl }: { manifestUrl: string }) { const locations: ServiceLocations = []; try { - const { data } = await axios.get>(manifestURL); + const { data } = await axios.get>(manifestUrl); Object.entries(data.locations).forEach(([locationId, location]) => { locations.push({ id: locationId, label: location.geo.name, geo: location.geo.location, + url: location.url, }); }); diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts new file mode 100644 index 00000000000000..55dd67088eb2c6 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts @@ -0,0 +1,111 @@ +/* + * 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 axios from 'axios'; +import { forkJoin, from as rxjsFrom, Observable, of } from 'rxjs'; +import { catchError, tap } from 'rxjs/operators'; +import { ServiceLocations, SyntheticsMonitorSavedObject } from '../../../common/types'; +import { getServiceLocations } from './get_service_locations'; +import { Logger } from '../../../../../../src/core/server'; + +const TEST_SERVICE_USERNAME = 'localKibanaIntegrationTestsUser'; + +export type MonitorConfigs = Array< + SyntheticsMonitorSavedObject['attributes'] & { + id: string; + source?: { + inline: { + script: string; + }; + }; + } +>; + +export interface ServiceData { + monitors: MonitorConfigs; + output: { + hosts: string[]; + api_key: string; + }; +} + +export class ServiceAPIClient { + private readonly username: string; + private readonly authorization: string; + private locations: ServiceLocations; + private logger: Logger; + + constructor(manifestUrl: string, username: string, password: string, logger: Logger) { + this.username = username; + this.authorization = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'); + this.logger = logger; + this.locations = []; + + getServiceLocations({ manifestUrl }).then((result) => { + this.locations = result; + }); + } + + async post(data: ServiceData) { + return this.callAPI('POST', data); + } + + async put(data: ServiceData) { + return this.callAPI('POST', data); + } + + async delete(data: ServiceData) { + return this.callAPI('DELETE', data); + } + + async callAPI(method: 'POST' | 'PUT' | 'DELETE', { monitors: allMonitors, output }: ServiceData) { + if (this.username === TEST_SERVICE_USERNAME) { + // we don't want to call service while local integration tests are running + return; + } + + const callServiceEndpoint = (monitors: ServiceData['monitors'], url: string) => { + return axios({ + method, + url: url + '/monitors', + data: { monitors, output }, + headers: { + Authorization: this.authorization, + }, + }); + }; + + const pushErrors: Array<{ locationId: string; error: Error }> = []; + + const promises: Array> = []; + + this.locations.forEach(({ id, url }) => { + const locMonitors = allMonitors.filter( + ({ locations }) => !locations || locations?.includes(id) + ); + if (locMonitors.length > 0) { + promises.push( + rxjsFrom(callServiceEndpoint(locMonitors, url)).pipe( + tap((result) => { + this.logger.debug(result.data); + }), + catchError((err) => { + pushErrors.push({ locationId: id, error: err }); + this.logger.error(err); + // we don't want to throw an unhandled exception here + return of(true); + }) + ) + ); + } + }); + + await forkJoin(promises).toPromise(); + + return pushErrors; + } +} diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts index ecec66db845c2f..cd5a72c2a14144 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts @@ -7,7 +7,8 @@ /* eslint-disable max-classes-per-file */ -import axios from 'axios'; +import { ValuesType } from 'utility-types'; + import { CoreStart, KibanaRequest, @@ -27,6 +28,7 @@ import { SyntheticsMonitorSavedObject } from '../../../common/types'; import { syntheticsMonitorType } from '../saved_objects/synthetics_monitor'; import { getEsHosts } from './get_es_hosts'; import { UptimeConfig } from '../../../common/config'; +import { MonitorConfigs, ServiceAPIClient } from './service_api_client'; const SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_TYPE = 'UPTIME:SyntheticsService:Sync-Saved-Monitor-Objects'; @@ -35,6 +37,7 @@ const SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_ID = 'UPTIME:SyntheticsService:sync- export class SyntheticsService { private logger: Logger; private readonly server: UptimeServerSetup; + private apiClient: ServiceAPIClient; private readonly config: UptimeConfig; private readonly esHosts: string[]; @@ -46,6 +49,10 @@ export class SyntheticsService { this.server = server; this.config = server.config; + const { manifestUrl, username, password } = this.config.unsafe.service; + + this.apiClient = new ServiceAPIClient(manifestUrl, username, password, logger); + this.esHosts = getEsHosts({ config: this.config, cloud: server.cloud }); } @@ -101,7 +108,7 @@ export class SyntheticsService { async run() { const { state } = taskInstance; - // TODO: Push API Key and Monitor Configs to service here + await service.pushConfigs(); return { state }; }, @@ -120,7 +127,7 @@ export class SyntheticsService { id: SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_ID, taskType: SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_TYPE, schedule: { - interval: '5m', + interval: '1m', }, params: {}, state: {}, @@ -137,7 +144,7 @@ export class SyntheticsService { }); } - async pushConfigs(request: KibanaRequest) { + async getOutput(request?: KibanaRequest) { if (!this.apiKey) { try { this.apiKey = await getAPIKeyForSyntheticsService({ server: this.server, request }); @@ -152,43 +159,92 @@ export class SyntheticsService { throw error; } - const monitors = await this.getMonitorConfigs(); + return { + hosts: this.esHosts, + api_key: `${this.apiKey.id}:${this.apiKey.apiKey}`, + }; + } + + async pushConfigs(request?: KibanaRequest, configs?: MonitorConfigs) { + const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs())); const data = { monitors, - output: { - hosts: this.esHosts, - api_key: `${this.apiKey.id}:${this.apiKey.apiKey}`, - }, + output: await this.getOutput(request), }; - const { url, username, password } = this.config.unsafe.service; - try { - await axios({ - method: 'POST', - url: url + '/monitors', - data, - headers: { - Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), - }, - }); + return await this.apiClient.post(data); } catch (e) { this.logger.error(e); + throw e; } } + async deleteConfigs(request: KibanaRequest, configs: MonitorConfigs) { + const data = { + monitors: configs, + output: await this.getOutput(request), + }; + return await this.apiClient.delete(data); + } + async getMonitorConfigs() { const savedObjectsClient = this.server.savedObjectsClient; - const monitorsSavedObjects = await savedObjectsClient.find({ + const monitorsSavedObjects = await savedObjectsClient.find< + SyntheticsMonitorSavedObject['attributes'] + >({ type: syntheticsMonitorType, }); const savedObjectsList = monitorsSavedObjects.saved_objects; - return savedObjectsList.map(({ attributes, id }) => ({ + return savedObjectsList.map>(({ attributes, id }) => ({ ...attributes, id, })); } + + formatConfigs(configs: MonitorConfigs) { + // TODO: Move to dedicated formatter class + function parseSchedule(schedule: any) { + if (schedule?.number) { + return `@every ${schedule.number}${schedule.unit}`; + } + return schedule; + } + + function parseUrl(urls?: string | string[]) { + if (!urls) { + return undefined; + } + if (urls instanceof Array) { + return urls; + } + return [urls]; + } + + function parseInlineSource(monAttrs: any) { + if (monAttrs['source.inline.script']) { + return { + inline: { + script: monAttrs['source.inline.script'], + }, + }; + } + } + return configs.map((monAttrs) => { + const { id, schedule, type, name, locations, tags, urls } = monAttrs; + return { + id, + type, + name, + locations, + tags, + source: parseInlineSource(monAttrs), + urls: parseUrl(urls), + schedule: parseSchedule(schedule), + }; + }); + } } class APIKeyMissingError extends Error { diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts index 6269c690a18adb..49658f3def5a12 100644 --- a/x-pack/plugins/uptime/server/plugin.ts +++ b/x-pack/plugins/uptime/server/plugin.ts @@ -27,6 +27,7 @@ import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_fie import { Dataset } from '../../rule_registry/server'; import { UptimeConfig } from '../common/config'; import { SyntheticsService } from './lib/synthetics_service/synthetics_service'; +import { syntheticsServiceApiKey } from './lib/saved_objects/service_api_key'; export type UptimeRuleRegistry = ReturnType['ruleRegistry']; @@ -89,9 +90,16 @@ export class Plugin implements PluginType { } public start(coreStart: CoreStart, plugins: UptimeCorePluginsStart) { - this.savedObjectsClient = new SavedObjectsClient( - coreStart.savedObjects.createInternalRepository() - ); + if (this.server?.config?.unsafe?.service.enabled) { + this.savedObjectsClient = new SavedObjectsClient( + coreStart.savedObjects.createInternalRepository([syntheticsServiceApiKey.name]) + ); + } else { + this.savedObjectsClient = new SavedObjectsClient( + coreStart.savedObjects.createInternalRepository() + ); + } + if (this.server) { this.server.security = plugins.security; this.server.fleet = plugins.fleet; @@ -102,6 +110,9 @@ export class Plugin implements PluginType { if (this.server?.config?.unsafe?.service.enabled) { this.syntheticService?.init(coreStart); this.syntheticService?.scheduleSyncTask(plugins.taskManager); + if (this.server && this.syntheticService) { + this.server.syntheticsService = this.syntheticService; + } } } diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts index 11d7dcedcaa34e..c30b4f96e81453 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts @@ -16,11 +16,21 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ validate: { body: schema.any(), }, - handler: async ({ request, savedObjectsClient }): Promise => { - const monitor = request.body as SyntheticsMonitorSavedObject; + handler: async ({ request, savedObjectsClient, server }): Promise => { + const monitor = request.body as SyntheticsMonitorSavedObject['attributes']; const newMonitor = await savedObjectsClient.create(syntheticsMonitorType, monitor); - // TODO: call to service sync + + const { syntheticsService } = server; + + const errors = await syntheticsService.pushConfigs(request, [ + { ...newMonitor.attributes, id: newMonitor.id }, + ]); + + if (errors) { + return errors; + } + return newMonitor; }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts index 68eb8aa130d2e5..c951acae216332 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts @@ -9,6 +9,7 @@ import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; import { syntheticsMonitorType } from '../../lib/saved_objects/synthetics_monitor'; +import { SyntheticsMonitorSavedObject } from '../../../common/types'; export const deleteSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ method: 'DELETE', @@ -18,17 +19,30 @@ export const deleteSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ monitorId: schema.string(), }), }, - handler: async ({ request, savedObjectsClient }): Promise => { + handler: async ({ request, savedObjectsClient, server }): Promise => { const { monitorId } = request.params; + const { syntheticsService } = server; + try { + const monitor = await savedObjectsClient.get( + syntheticsMonitorType, + monitorId + ); + await savedObjectsClient.delete(syntheticsMonitorType, monitorId); - // TODO: call to service sync + const errors = await syntheticsService.deleteConfigs(request, [ + { ...monitor.attributes, id: monitorId }, + ]); + if (errors) { + return errors; + } return monitorId; } catch (getErr) { if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { return 'Not found'; } + throw getErr; } }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts index 46a91738c380d0..684838ed4efe05 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts @@ -19,13 +19,26 @@ export const editSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ }), body: schema.any(), }, - handler: async ({ request, savedObjectsClient }): Promise => { + handler: async ({ request, savedObjectsClient, server }): Promise => { const monitor = request.body as SyntheticsMonitorSavedObject['attributes']; const { monitorId } = request.params; + const { syntheticsService } = server; + const editMonitor = await savedObjectsClient.update(syntheticsMonitorType, monitorId, monitor); - // TODO: call to service sync + + const errors = await syntheticsService.pushConfigs(request, [ + { + ...(editMonitor.attributes as SyntheticsMonitorSavedObject['attributes']), + id: editMonitor.id, + }, + ]); + + if (errors) { + return errors; + } + return editMonitor; }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_service_locations.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_service_locations.ts index b96b98870e38eb..b63f89b490dc9f 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/get_service_locations.ts @@ -13,5 +13,6 @@ export const getServiceLocationsRoute: UMRestApiRouteFactory = () => ({ method: 'GET', path: API_URLS.SERVICE_LOCATIONS, validate: {}, - handler: async ({ server }): Promise => getServiceLocations({ config: server.config }), + handler: async ({ server }): Promise => + getServiceLocations({ manifestUrl: server.config.service.manifestUrl }), }); diff --git a/x-pack/test/api_integration/config.ts b/x-pack/test/api_integration/config.ts index bf42a5b0865a21..dde3b827bcdae2 100644 --- a/x-pack/test/api_integration/config.ts +++ b/x-pack/test/api_integration/config.ts @@ -38,7 +38,7 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi '--xpack.uptime.unsafe.service.enabled=true', '--xpack.uptime.unsafe.service.password=test', '--xpack.uptime.unsafe.service.manifestUrl=http://test.com', - '--xpack.uptime.unsafe.service.username=user', + '--xpack.uptime.unsafe.service.username=localKibanaIntegrationTestsUser', `--xpack.securitySolution.enableExperimental=${JSON.stringify(['ruleRegistryEnabled'])}`, ], }, From 976253a5f23cc4251f11c275984c74d221ad3f8d Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 2 Dec 2021 10:56:06 +0100 Subject: [PATCH 11/20] [Lens] Improve column type detection in table for alignment (#120007) * :bug: Fix last value alignment behaviour * :white_check_mark: Add unit test --- .../components/table_basic.test.tsx | 46 +++++++++++++++++++ .../components/table_basic.tsx | 9 +++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index f71d2b9ec63262..19315b5835d5fb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -508,6 +508,52 @@ describe('DatatableComponent', () => { }); }); + test('it detect last_value filtered metric type', () => { + const { data, args } = sampleArgs(); + + const table = data.tables.l1; + const column = table.columns[1]; + + column.meta = { + ...column.meta, + field: undefined, + type: 'number', + sourceParams: { ...column.meta.sourceParams, type: 'filtered_metric' }, + }; + table.rows[0].b = 'Hello'; + + const wrapper = shallow( + ({ convert: (x) => x } as IFieldFormat)} + dispatchEvent={onDispatchEvent} + getType={jest.fn()} + renderMode="view" + paletteService={chartPluginMock.createPaletteRegistry()} + uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} + /> + ); + + expect(wrapper.find(DataContext.Provider).prop('value').alignments).toEqual({ + // set via args + a: 'center', + // default for string + b: 'left', + // default for number + c: 'right', + }); + }); + test('it should refresh the table header when the datatable data changes', () => { const { data, args } = sampleArgs(); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index 6635ada9a40363..7ceffcaaff5dbd 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -212,7 +212,14 @@ export const DatatableComponent = (props: DatatableRenderProps) => { const isNumericMap: Record = useMemo(() => { const numericMap: Record = {}; for (const column of firstLocalTable.columns) { - numericMap[column.id] = column.meta.type === 'number'; + // filtered metrics result as "number" type, but have no field + numericMap[column.id] = + (column.meta.type === 'number' && column.meta.field != null) || + // as fallback check the first available value type + // mind here: date can be seen as numbers, to carefully check that is a filtered metric + (column.meta.field == null && + typeof firstLocalTable.rows.find((row) => row[column.id] != null)?.[column.id] === + 'number'); } return numericMap; }, [firstLocalTable]); From acbee9bc491ec0ac99a2c056503404242ce4b708 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Thu, 2 Dec 2021 11:03:59 +0100 Subject: [PATCH 12/20] [Security Solution][Detections] Add basic error handling to logging methods of Rule Execution Log (#120157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Ticket:** https://github.com/elastic/kibana/issues/119595 ## Summary With this PR, calling `IRuleExecutionLog.logStatusChange()` is now safe for callers (rule executors or route handlers). It doesn't throw and writes any exceptions being caught to Kibana logs. Example error in the logs: ``` [2021-12-01T20:05:36.620+01:00][DEBUG][plugins.securitySolution] ... [2021-12-01T20:05:37.591+01:00][ERROR][plugins.securitySolution.ruleExecution] Error logging rule execution status change; status: "succeeded", rule id: "abea42f0-489b-11ec-83fa-1bde56c47ca8", rule name: "SDH-247 my loooooong loooooong name"; Error: TESTING ERROR HANDLING at EventLogAdapter.logStatusChangeToSavedObjects (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts:91:11) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Promise.all (index 0) at EventLogAdapter.logStatusChange (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts:83:5) at RuleExecutionLogClient.logStatusChange (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts:91:7) at Object.executor (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts:345:15) at Object.executor (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts:20:23) at TaskRunner.executeAlertInstances (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:316:31) at promiseResult (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/lib/result_type.ts:47:17) at TaskRunner.loadAlertAttributesAndRun (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:569:14) at errorAsAlertTaskRunResult (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:1119:12) at TaskRunner.run (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:635:33) at TaskManagerRunner.run (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/task_manager/server/task_running/task_runner.ts:297:22) [2021-12-01T20:05:37.591+01:00][DEBUG][plugins.securitySolution] ... ``` ## Additional log metadata For debugging purposes, rule execution log client writes additional log metadata with any caught exception. It's not visible in Kibana console logs by default, but can be enabled in Kibana config. I used the following config to test it: ```yaml logging: appenders: custom_console: type: console layout: type: pattern highlight: true pattern: "[%date][%level][%logger] %message %meta" root: appenders: [custom_console] level: warn loggers: - name: config level: info - name: plugins level: warn - name: plugins.securitySolution level: debug - name: plugins.ruleRegistry level: info - name: plugins.alerting level: info - name: plugins.taskManager level: info - name: savedobjects-service level: info - name: elasticsearch-service level: off - name: elasticsearch level: off - name: elasticsearch.query level: off - name: server level: fatal - name: optimize level: info - name: metrics.ops level: off ``` Error in the logs with additional metadata (find it after the stack trace): ``` [2021-12-01T20:03:30.807+01:00][DEBUG][plugins.securitySolution] ... [2021-12-01T20:03:31.812+01:00][ERROR][plugins.securitySolution.ruleExecution] Error logging rule execution status change; status: "succeeded", rule id: "abea42f0-489b-11ec-83fa-1bde56c47ca8", rule name: "SDH-247 my loooooong loooooong name"; Error: TESTING ERROR HANDLING at EventLogAdapter.logStatusChangeToSavedObjects (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts:91:11) at runMicrotasks () at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Promise.all (index 0) at EventLogAdapter.logStatusChange (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts:83:5) at RuleExecutionLogClient.logStatusChange (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts:93:7) at Object.executor (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts:345:15) at Object.executor (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts:20:23) at TaskRunner.executeAlertInstances (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:316:31) at promiseResult (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/lib/result_type.ts:47:17) at TaskRunner.loadAlertAttributesAndRun (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:569:14) at errorAsAlertTaskRunResult (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:1119:12) at TaskRunner.run (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/alerting/server/task_runner/task_runner.ts:635:33) at TaskManagerRunner.run (/Users/georgii/Code/elastic/kibana-main/x-pack/plugins/task_manager/server/task_running/task_runner.ts:297:22) {"rule":{"id":"abea42f0-489b-11ec-83fa-1bde56c47ca8","name":"SDH-247 my loooooong loooooong name","type":"siem.queryRule","execution":{"status":"succeeded"}},"kibana":{"spaceId":"default"}} [2021-12-01T20:03:31.812+01:00][DEBUG][plugins.securitySolution] ... ``` Here it is extracted from the log: ``` {"rule":{"id":"abea42f0-489b-11ec-83fa-1bde56c47ca8","name":"SDH-247 my loooooong loooooong name","type":"siem.queryRule","execution":{"status":"succeeded"}},"kibana":{"spaceId":"default"}} ``` More info on the layout configuration: https://www.elastic.co/guide/en/kibana/master/logging-configuration.html#pattern-layout I couldn't decently pretty print it with the config options that are available 😞 I was thinking what to include in the log metadata and what to include in the regular log message, and my thoughts so far were: - It could be useful to add important attributes like correlation ids (rule id etc) and details of the original exception directly into the log message, because that's what is going to be logged out of the box with the default logging configuration. - Log meta could be used for logging optional attributes that could be useful when digging deeper into an issue when debugging Security app and Detection Engine. ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../event_log_adapter/event_log_adapter.ts | 43 +++++++++------- .../rule_execution_log_client.ts | 49 +++++++++++++++---- .../rule_execution_log/types.ts | 17 ++++++- .../create_security_rule_type_wrapper.ts | 1 + .../signals/signal_rule_alert_type.ts | 1 + .../security_solution/server/plugin.ts | 2 +- .../server/request_context_factory.ts | 6 ++- 7 files changed, 89 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts index f622ea9248b24b..d88f4119b691fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts @@ -79,7 +79,32 @@ export class EventLogAdapter implements IRuleExecutionLogClient { // EventLog execution events are immutable, nothing to do here } - private async logExecutionMetrics(args: LogExecutionMetricsArgs) { + public async logStatusChange(args: LogStatusChangeArgs): Promise { + await Promise.all([ + this.logStatusChangeToSavedObjects(args), + this.logStatusChangeToEventLog(args), + ]); + } + + private async logStatusChangeToSavedObjects(args: LogStatusChangeArgs): Promise { + await this.savedObjectsAdapter.logStatusChange(args); + } + + private async logStatusChangeToEventLog(args: LogStatusChangeArgs): Promise { + if (args.metrics) { + this.logExecutionMetrics({ + ruleId: args.ruleId, + ruleName: args.ruleName, + ruleType: args.ruleType, + spaceId: args.spaceId, + metrics: args.metrics, + }); + } + + this.eventLogClient.logStatusChange(args); + } + + private logExecutionMetrics(args: LogExecutionMetricsArgs): void { const { ruleId, spaceId, ruleType, ruleName, metrics } = args; this.eventLogClient.logExecutionMetrics({ @@ -98,20 +123,4 @@ export class EventLogAdapter implements IRuleExecutionLogClient { }, }); } - - public async logStatusChange(args: LogStatusChangeArgs) { - await this.savedObjectsAdapter.logStatusChange(args); - - if (args.metrics) { - await this.logExecutionMetrics({ - ruleId: args.ruleId, - ruleName: args.ruleName, - ruleType: args.ruleType, - spaceId: args.spaceId, - metrics: args.metrics, - }); - } - - this.eventLogClient.logStatusChange(args); - } } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts index bc77c6e44fa56c..f3321580aa0524 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsClientContract } from '../../../../../../../src/core/server'; +import { Logger, SavedObjectsClientContract } from 'src/core/server'; import { IEventLogClient, IEventLogService } from '../../../../../event_log/server'; import { IRuleStatusSOAttributes } from '../rules/types'; import { EventLogAdapter } from './event_log_adapter/event_log_adapter'; @@ -20,6 +20,7 @@ import { GetCurrentStatusArgs, GetCurrentStatusBulkArgs, GetCurrentStatusBulkResult, + ExtMeta, } from './types'; import { truncateMessage } from './utils/normalization'; @@ -28,13 +29,16 @@ interface ConstructorParams { savedObjectsClient: SavedObjectsClientContract; eventLogService: IEventLogService; eventLogClient?: IEventLogClient; + logger: Logger; } export class RuleExecutionLogClient implements IRuleExecutionLogClient { - private client: IRuleExecutionLogClient; + private readonly client: IRuleExecutionLogClient; + private readonly logger: Logger; constructor(params: ConstructorParams) { - const { underlyingClient, eventLogService, eventLogClient, savedObjectsClient } = params; + const { underlyingClient, eventLogService, eventLogClient, savedObjectsClient, logger } = + params; switch (underlyingClient) { case UnderlyingLogClient.savedObjects: @@ -44,6 +48,10 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { this.client = new EventLogAdapter(eventLogService, eventLogClient, savedObjectsClient); break; } + + // We write rule execution logs via a child console logger with the context + // "plugins.securitySolution.ruleExecution" + this.logger = logger.get('ruleExecution'); } /** @deprecated */ @@ -74,11 +82,34 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { return this.client.deleteCurrentStatus(ruleId); } - public async logStatusChange(args: LogStatusChangeArgs) { - const message = args.message ? truncateMessage(args.message) : args.message; - return this.client.logStatusChange({ - ...args, - message, - }); + public async logStatusChange(args: LogStatusChangeArgs): Promise { + const { newStatus, message, ruleId, ruleName, ruleType, spaceId } = args; + + try { + const truncatedMessage = message ? truncateMessage(message) : message; + await this.client.logStatusChange({ + ...args, + message: truncatedMessage, + }); + } catch (e) { + const logMessage = 'Error logging rule execution status change'; + const logAttributes = `status: "${newStatus}", rule id: "${ruleId}", rule name: "${ruleName}"`; + const logReason = e instanceof Error ? `${e.stack}` : `${e}`; + const logMeta: ExtMeta = { + rule: { + id: ruleId, + name: ruleName, + type: ruleType, + execution: { + status: newStatus, + }, + }, + kibana: { + spaceId, + }, + }; + + this.logger.error(`${logMessage}; ${logAttributes}; ${logReason}`, logMeta); + } } } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts index 06a0b90985af73..1fa256b0f088c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts @@ -6,7 +6,7 @@ */ import { Duration } from 'moment'; -import { SavedObjectsFindResult } from '../../../../../../../src/core/server'; +import { LogMeta, SavedObjectsFindResult } from 'src/core/server'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { IRuleStatusSOAttributes } from '../rules/types'; @@ -103,3 +103,18 @@ export interface ExecutionMetrics { lastLookBackDate?: string; executionGap?: Duration; } + +/** + * Custom extended log metadata that rule execution logger can attach to every log record. + */ +export type ExtMeta = LogMeta & { + rule?: LogMeta['rule'] & { + type?: string; + execution?: { + status?: RuleExecutionStatus; + }; + }; + kibana?: { + spaceId?: string; + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index bc13a12e01ca4a..daab0540c23af5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -70,6 +70,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = underlyingClient: config.ruleExecutionLog.underlyingClient, savedObjectsClient, eventLogService, + logger, }); const completeRule = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 85285eed2817ac..f49c6327483e3d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -149,6 +149,7 @@ export const signalRulesAlertType = ({ underlyingClient: config.ruleExecutionLog.underlyingClient, savedObjectsClient: services.savedObjectsClient, eventLogService, + logger, }); const completeRule: CompleteRule = { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 5e7bf0659947c1..dfff89a90c7324 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -140,7 +140,7 @@ export class Plugin implements ISecuritySolutionPlugin { const eventLogService = plugins.eventLog; registerEventLogProvider(eventLogService); - const requestContextFactory = new RequestContextFactory({ config, core, plugins }); + const requestContextFactory = new RequestContextFactory({ config, logger, core, plugins }); const router = core.http.createRouter(); core.http.registerRouteHandlerContext( APP_ID, diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index b7586ee9596526..f6c1d6b44eca62 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { Logger, KibanaRequest, RequestHandlerContext } from 'kibana/server'; import { ExceptionListClient } from '../../lists/server'; import { DEFAULT_SPACE_ID } from '../common/constants'; @@ -28,6 +28,7 @@ export interface IRequestContextFactory { interface ConstructorOptions { config: ConfigType; + logger: Logger; core: SecuritySolutionPluginCoreSetupDependencies; plugins: SecuritySolutionPluginSetupDependencies; } @@ -44,7 +45,7 @@ export class RequestContextFactory implements IRequestContextFactory { request: KibanaRequest ): Promise { const { options, appClientFactory } = this; - const { config, core, plugins } = options; + const { config, logger, core, plugins } = options; const { lists, ruleRegistry, security } = plugins; const [, startPlugins] = await core.getStartServices(); @@ -73,6 +74,7 @@ export class RequestContextFactory implements IRequestContextFactory { savedObjectsClient: context.core.savedObjects.client, eventLogService: plugins.eventLog, eventLogClient: startPlugins.eventLog.getClient(request), + logger, }), getExceptionListClient: () => { From 9814a8191040e339f89babf2c19220be3e62b369 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 2 Dec 2021 11:11:07 +0100 Subject: [PATCH 13/20] [Lens] Show generic error for invalid time shift string (#120077) * :bug: Show generic error for invalid string * :white_check_mark: add unit test --- .../dimension_panel/dimension_panel.test.tsx | 17 +++++++++++++++++ .../dimension_panel/time_shift.tsx | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 06c1bb931f730e..2ff2fd67435ab1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -1485,6 +1485,23 @@ describe('IndexPatternDimensionEditorPanel', () => { }, }); }); + + it('should report a generic error for invalid shift string', () => { + const props = getProps({ + timeShift: '5 months', + }); + wrapper = mount(); + + expect(wrapper.find(TimeShift).find(EuiComboBox).prop('isInvalid')).toBeTruthy(); + + expect( + wrapper + .find(TimeShift) + .find('[data-test-subj="indexPattern-dimension-time-shift-row"]') + .first() + .prop('error') + ).toBe('Time shift value is not valid.'); + }); }); describe('filtering', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx index 36cc4a3c22e440..7055d39f80aa1f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_shift.tsx @@ -129,6 +129,7 @@ export function TimeShift({ Date: Thu, 2 Dec 2021 10:49:55 +0000 Subject: [PATCH 14/20] [Uptime] Add throttling parameters to the Fleet UI (#118594) * [Uptime] add throttling parameters to the browser synthetic monitor form This commit adds three fields to the browser synthetic monitor creation form, these fields are: `download_speed`, `upload_speed`, and `latency`. Each of these fields is used to test how applications behave under particular network conditions. This commit closes #114155. * [Uptime] add functional tests for synthetic monitors throttling fields * [Uptime] add toggling for throttling fields * [Uptime] add normalizer for editing browser throttling fields --- .../step_select_agent_policy.tsx | 2 +- .../browser/advanced_fields.test.tsx | 44 ++-- .../fleet_package/browser/advanced_fields.tsx | 16 +- .../fleet_package/browser/formatters.ts | 20 ++ .../fleet_package/browser/normalizers.test.ts | 78 ++++++++ .../fleet_package/browser/normalizers.ts | 42 +++- .../browser/throttling_fields.test.tsx | 171 ++++++++++++++++ .../browser/throttling_fields.tsx | 188 ++++++++++++++++++ .../contexts/browser_context_advanced.tsx | 5 + .../fleet_package/custom_fields.test.tsx | 14 +- .../fleet_package/custom_fields.tsx | 2 +- .../hooks/use_update_policy.test.tsx | 25 +++ .../public/components/fleet_package/types.tsx | 27 +++ .../components/fleet_package/validation.tsx | 11 + .../monitor_management/formatters/browser.ts | 5 + .../apps/uptime/synthetics_integration.ts | 78 ++++++++ .../synthetics_integration_page.ts | 36 +++- 17 files changed, 737 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx index 58af86b09ed032..c46ef8893fa40f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx @@ -229,7 +229,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{

diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx index fabf6da49cf479..f0900ae2595926 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx @@ -6,18 +6,29 @@ */ import React from 'react'; -import { fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; import { BrowserAdvancedFields } from './advanced_fields'; -import { ConfigKeys, IBrowserAdvancedFields, IBrowserSimpleFields } from '../types'; +import { + ConfigKeys, + DataStream, + IBrowserAdvancedFields, + IBrowserSimpleFields, + Validation, +} from '../types'; import { BrowserAdvancedFieldsContextProvider, BrowserSimpleFieldsContextProvider, defaultBrowserAdvancedFields as defaultConfig, defaultBrowserSimpleFields, } from '../contexts'; +import { validate as centralValidation } from '../validation'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +const defaultValidation = centralValidation[DataStream.BROWSER]; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), htmlIdGenerator: () => () => `id-${Math.random()}`, })); @@ -25,16 +36,20 @@ describe('', () => { const WrappedComponent = ({ defaultValues = defaultConfig, defaultSimpleFields = defaultBrowserSimpleFields, + validate = defaultValidation, }: { defaultValues?: IBrowserAdvancedFields; defaultSimpleFields?: IBrowserSimpleFields; + validate?: Validation; }) => { return ( - - - - - + + + + + + + ); }; @@ -43,18 +58,19 @@ describe('', () => { const syntheticsArgs = getByLabelText('Synthetics args'); const screenshots = getByLabelText('Screenshot options') as HTMLInputElement; + expect(screenshots.value).toEqual(defaultConfig[ConfigKeys.SCREENSHOTS]); expect(syntheticsArgs).toBeInTheDocument(); }); - it('handles changing fields', () => { - const { getByLabelText } = render(); - - const screenshots = getByLabelText('Screenshot options') as HTMLInputElement; - - fireEvent.change(screenshots, { target: { value: 'off' } }); + describe('handles changing fields', () => { + it('for screenshot options', () => { + const { getByLabelText } = render(); - expect(screenshots.value).toEqual('off'); + const screenshots = getByLabelText('Screenshot options') as HTMLInputElement; + userEvent.selectOptions(screenshots, ['off']); + expect(screenshots.value).toEqual('off'); + }); }); it('only displayed filter options when zip url is truthy', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx index cbbc9a33642b5e..0133642b8c4b37 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React, { memo, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiAccordion, @@ -20,11 +20,16 @@ import { ComboBox } from '../combo_box'; import { useBrowserAdvancedFieldsContext, useBrowserSimpleFieldsContext } from '../contexts'; -import { ConfigKeys, ScreenshotOption } from '../types'; +import { ConfigKeys, Validation, ScreenshotOption } from '../types'; import { OptionalLabel } from '../optional_label'; +import { ThrottlingFields } from './throttling_fields'; -export const BrowserAdvancedFields = () => { +interface Props { + validate: Validation; +} + +export const BrowserAdvancedFields = memo(({ validate }) => { const { fields, setFields } = useBrowserAdvancedFieldsContext(); const { fields: simpleFields } = useBrowserSimpleFieldsContext(); @@ -156,6 +161,7 @@ export const BrowserAdvancedFields = () => { } />
+ { /> + + ); -}; +}); const requestMethodOptions = Object.values(ScreenshotOption).map((option) => ({ value: option, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts index e457453a38f195..5384122a186218 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts @@ -21,6 +21,21 @@ import { export type BrowserFormatMap = Record; +const throttlingFormatter: Formatter = (fields) => { + if (!fields[ConfigKeys.IS_THROTTLING_ENABLED]) return 'false'; + + const getThrottlingValue = (v: string | undefined, suffix: 'd' | 'u' | 'l') => + v !== '' && v !== undefined ? `${v}${suffix}` : null; + + return [ + getThrottlingValue(fields[ConfigKeys.DOWNLOAD_SPEED], 'd'), + getThrottlingValue(fields[ConfigKeys.UPLOAD_SPEED], 'u'), + getThrottlingValue(fields[ConfigKeys.LATENCY], 'l'), + ] + .filter((v) => v !== null) + .join('/'); +}; + export const browserFormatters: BrowserFormatMap = { [ConfigKeys.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKeys.METADATA]), [ConfigKeys.SOURCE_ZIP_URL]: null, @@ -31,6 +46,10 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKeys.SOURCE_INLINE]: (fields) => stringToJsonFormatter(fields[ConfigKeys.SOURCE_INLINE]), [ConfigKeys.PARAMS]: null, [ConfigKeys.SCREENSHOTS]: null, + [ConfigKeys.IS_THROTTLING_ENABLED]: null, + [ConfigKeys.DOWNLOAD_SPEED]: null, + [ConfigKeys.UPLOAD_SPEED]: null, + [ConfigKeys.LATENCY]: null, [ConfigKeys.SYNTHETICS_ARGS]: (fields) => arrayToJsonFormatter(fields[ConfigKeys.SYNTHETICS_ARGS]), [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: (fields) => @@ -49,6 +68,7 @@ export const browserFormatters: BrowserFormatMap = { stringToJsonFormatter(fields[ConfigKeys.JOURNEY_FILTERS_MATCH]), [ConfigKeys.JOURNEY_FILTERS_TAGS]: (fields) => arrayToJsonFormatter(fields[ConfigKeys.JOURNEY_FILTERS_TAGS]), + [ConfigKeys.THROTTLING_CONFIG]: throttlingFormatter, [ConfigKeys.IGNORE_HTTPS_ERRORS]: null, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts new file mode 100644 index 00000000000000..49a08a12454e36 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { ConfigKeys } from '../types'; +import { getThrottlingParamNormalizer, isThrottlingEnabledNormalizer } from './normalizers'; +import { defaultBrowserAdvancedFields } from '../contexts'; + +describe('browser normalizers', () => { + const makeThrottlingConfig = (value: string) => ({ + [ConfigKeys.THROTTLING_CONFIG]: { value }, + }); + + describe('throttlingToParameterNormalizer', () => { + it('can extract download values', () => { + const fields = makeThrottlingConfig('10d/5u/2.5l'); + + expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(fields)).toEqual('10'); + }); + + it('can extract upload values', () => { + const fields = makeThrottlingConfig('10d/5u/2.5l'); + + expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(fields)).toEqual('5'); + }); + + it('can extract latency values', () => { + const fields = makeThrottlingConfig('10d/5u/2.5l'); + + expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(fields)).toEqual('2.5'); + }); + + it('returns default values when throttling is disabled', () => { + const fields = makeThrottlingConfig('false'); + + expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.DOWNLOAD_SPEED] + ); + expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.UPLOAD_SPEED] + ); + expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.LATENCY] + ); + }); + + it("returns default values when the desired suffix doesn't exist", () => { + const noUploadFields = makeThrottlingConfig('10d/2.5l'); + expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(noUploadFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.UPLOAD_SPEED] + ); + + const noDownloadFields = makeThrottlingConfig('10u/2.5l'); + expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(noDownloadFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.DOWNLOAD_SPEED] + ); + + const noLatencyFields = makeThrottlingConfig('10d/5u'); + expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(noLatencyFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKeys.LATENCY] + ); + }); + }); + + describe('isThrottlingEnabledNormalizer', () => { + it('returns true for any value that is not "false"', () => { + expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('10d/2l'))).toEqual(true); + expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('test'))).toEqual(true); + }); + + it('returns false when throttling config is the string "false"', () => { + expect(isThrottlingEnabledNormalizer(makeThrottlingConfig('false'))).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts index 2c675b9f288042..6d8b35673fba36 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts @@ -5,7 +5,13 @@ * 2.0. */ -import { BrowserFields, ConfigKeys } from '../types'; +import { + BrowserFields, + ConfigKeys, + ThrottlingSuffix, + ThrottlingConfigKey, + configKeyToThrottlingSuffix, +} from '../types'; import { Normalizer, commonNormalizers, @@ -30,6 +36,35 @@ export const getBrowserJsonToJavascriptNormalizer = (key: ConfigKeys) => { return getJsonToJavascriptNormalizer(key, defaultBrowserFields); }; +export function throttlingToParameterNormalizer( + suffix: ThrottlingSuffix, + throttlingConfigValue?: string +): unknown { + if (!throttlingConfigValue || throttlingConfigValue === 'false') return null; + return ( + throttlingConfigValue + .split('/') + .filter((p) => p.endsWith(suffix))[0] + ?.slice(0, -1) ?? null + ); +} + +export const isThrottlingEnabledNormalizer: Normalizer = function isThrottlingEnabledNormalizer( + fields +) { + const throttlingEnabled = fields?.[ConfigKeys.THROTTLING_CONFIG]?.value; + + // If we have any value that's not an explicit "false" it means throttling is "on" + return throttlingEnabled !== 'false'; +}; + +export function getThrottlingParamNormalizer(key: ThrottlingConfigKey): Normalizer { + const paramSuffix = configKeyToThrottlingSuffix[key]; + return (fields) => + throttlingToParameterNormalizer(paramSuffix, fields?.[ConfigKeys.THROTTLING_CONFIG]?.value) ?? + defaultBrowserFields[key]; +} + export const browserNormalizers: BrowserNormalizerMap = { [ConfigKeys.METADATA]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.METADATA), [ConfigKeys.SOURCE_ZIP_URL]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_URL), @@ -41,6 +76,11 @@ export const browserNormalizers: BrowserNormalizerMap = { [ConfigKeys.PARAMS]: getBrowserNormalizer(ConfigKeys.PARAMS), [ConfigKeys.SCREENSHOTS]: getBrowserNormalizer(ConfigKeys.SCREENSHOTS), [ConfigKeys.SYNTHETICS_ARGS]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.SYNTHETICS_ARGS), + [ConfigKeys.IS_THROTTLING_ENABLED]: isThrottlingEnabledNormalizer, + [ConfigKeys.DOWNLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED), + [ConfigKeys.UPLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED), + [ConfigKeys.LATENCY]: getThrottlingParamNormalizer(ConfigKeys.LATENCY), + [ConfigKeys.THROTTLING_CONFIG]: getBrowserNormalizer(ConfigKeys.THROTTLING_CONFIG), [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: getBrowserJsonToJavascriptNormalizer( ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES ), diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx new file mode 100644 index 00000000000000..b27e848c98a139 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx @@ -0,0 +1,171 @@ +/* + * 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 React from 'react'; +import userEvent from '@testing-library/user-event'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { ThrottlingFields } from './throttling_fields'; +import { DataStream, IBrowserAdvancedFields, IBrowserSimpleFields, Validation } from '../types'; +import { + BrowserAdvancedFieldsContextProvider, + BrowserSimpleFieldsContextProvider, + defaultBrowserAdvancedFields as defaultConfig, + defaultBrowserSimpleFields, +} from '../contexts'; +import { validate as centralValidation } from '../validation'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +const defaultValidation = centralValidation[DataStream.BROWSER]; + +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), + htmlIdGenerator: () => () => `id-${Math.random()}`, +})); + +describe('', () => { + const WrappedComponent = ({ + defaultValues = defaultConfig, + defaultSimpleFields = defaultBrowserSimpleFields, + validate = defaultValidation, + }: { + defaultValues?: IBrowserAdvancedFields; + defaultSimpleFields?: IBrowserSimpleFields; + validate?: Validation; + }) => { + return ( + + + + + + + + ); + }; + + it('renders ThrottlingFields', () => { + const { getByLabelText, getByTestId } = render(); + + const enableSwitch = getByTestId('syntheticsBrowserIsThrottlingEnabled'); + const downloadSpeed = getByLabelText('Download Speed'); + const uploadSpeed = getByLabelText('Upload Speed'); + const latency = getByLabelText('Latency'); + + expect(enableSwitch).toBeChecked(); + expect(downloadSpeed).toBeInTheDocument(); + expect(uploadSpeed).toBeInTheDocument(); + expect(latency).toBeInTheDocument(); + }); + + describe('handles changing fields', () => { + it('for the enable switch', () => { + const { getByTestId } = render(); + + const enableSwitch = getByTestId('syntheticsBrowserIsThrottlingEnabled'); + userEvent.click(enableSwitch); + expect(enableSwitch).not.toBeChecked(); + }); + + it('for the download option', () => { + const { getByLabelText } = render(); + + const downloadSpeed = getByLabelText('Download Speed') as HTMLInputElement; + userEvent.clear(downloadSpeed); + userEvent.type(downloadSpeed, '1337'); + expect(downloadSpeed.value).toEqual('1337'); + }); + + it('for the upload option', () => { + const { getByLabelText } = render(); + + const uploadSpeed = getByLabelText('Upload Speed') as HTMLInputElement; + userEvent.clear(uploadSpeed); + userEvent.type(uploadSpeed, '1338'); + expect(uploadSpeed.value).toEqual('1338'); + }); + + it('for the latency option', () => { + const { getByLabelText } = render(); + const latency = getByLabelText('Latency') as HTMLInputElement; + userEvent.clear(latency); + userEvent.type(latency, '1339'); + expect(latency.value).toEqual('1339'); + }); + }); + + describe('validates changing fields', () => { + it('disallows negative/zero download speeds', () => { + const { getByLabelText, queryByText } = render(); + + const downloadSpeed = getByLabelText('Download Speed') as HTMLInputElement; + userEvent.clear(downloadSpeed); + userEvent.type(downloadSpeed, '-1337'); + expect(queryByText('Download speed must be greater than zero.')).toBeInTheDocument(); + + userEvent.clear(downloadSpeed); + userEvent.type(downloadSpeed, '0'); + expect(queryByText('Download speed must be greater than zero.')).toBeInTheDocument(); + + userEvent.clear(downloadSpeed); + userEvent.type(downloadSpeed, '1'); + expect(queryByText('Download speed must be greater than zero.')).not.toBeInTheDocument(); + }); + + it('disallows negative/zero upload speeds', () => { + const { getByLabelText, queryByText } = render(); + + const uploadSpeed = getByLabelText('Upload Speed') as HTMLInputElement; + userEvent.clear(uploadSpeed); + userEvent.type(uploadSpeed, '-1337'); + expect(queryByText('Upload speed must be greater than zero.')).toBeInTheDocument(); + + userEvent.clear(uploadSpeed); + userEvent.type(uploadSpeed, '0'); + expect(queryByText('Upload speed must be greater than zero.')).toBeInTheDocument(); + + userEvent.clear(uploadSpeed); + userEvent.type(uploadSpeed, '1'); + expect(queryByText('Upload speed must be greater than zero.')).not.toBeInTheDocument(); + }); + + it('disallows negative latency values', () => { + const { getByLabelText, queryByText } = render(); + + const latency = getByLabelText('Latency') as HTMLInputElement; + userEvent.clear(latency); + userEvent.type(latency, '-1337'); + expect(queryByText('Latency must not be negative.')).toBeInTheDocument(); + + userEvent.clear(latency); + userEvent.type(latency, '0'); + expect(queryByText('Latency must not be negative.')).not.toBeInTheDocument(); + + userEvent.clear(latency); + userEvent.type(latency, '1'); + expect(queryByText('Latency must not be negative.')).not.toBeInTheDocument(); + }); + }); + + it('only displays download, upload, and latency fields with throttling is on', () => { + const { getByLabelText, getByTestId } = render(); + + const enableSwitch = getByTestId('syntheticsBrowserIsThrottlingEnabled'); + const downloadSpeed = getByLabelText('Download Speed'); + const uploadSpeed = getByLabelText('Upload Speed'); + const latency = getByLabelText('Latency'); + + expect(downloadSpeed).toBeInTheDocument(); + expect(uploadSpeed).toBeInTheDocument(); + expect(latency).toBeInTheDocument(); + + userEvent.click(enableSwitch); + + expect(downloadSpeed).not.toBeInTheDocument(); + expect(uploadSpeed).not.toBeInTheDocument(); + expect(latency).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx new file mode 100644 index 00000000000000..19bfd961f64616 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx @@ -0,0 +1,188 @@ +/* + * 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 React, { memo, useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiDescribedFormGroup, + EuiSwitch, + EuiSpacer, + EuiFormRow, + EuiFieldNumber, + EuiText, +} from '@elastic/eui'; + +import { OptionalLabel } from '../optional_label'; +import { useBrowserAdvancedFieldsContext } from '../contexts'; +import { Validation, ConfigKeys } from '../types'; + +interface Props { + validate: Validation; +} + +type ThrottlingConfigs = + | ConfigKeys.IS_THROTTLING_ENABLED + | ConfigKeys.DOWNLOAD_SPEED + | ConfigKeys.UPLOAD_SPEED + | ConfigKeys.LATENCY; + +export const ThrottlingFields = memo(({ validate }) => { + const { fields, setFields } = useBrowserAdvancedFieldsContext(); + + const handleInputChange = useCallback( + ({ value, configKey }: { value: unknown; configKey: ThrottlingConfigs }) => { + setFields((prevFields) => ({ ...prevFields, [configKey]: value })); + }, + [setFields] + ); + + const throttlingInputs = fields[ConfigKeys.IS_THROTTLING_ENABLED] ? ( + <> + + + } + labelAppend={} + isInvalid={!!validate[ConfigKeys.DOWNLOAD_SPEED]?.(fields)} + error={ + + } + > + { + handleInputChange({ + value: event.target.value, + configKey: ConfigKeys.DOWNLOAD_SPEED, + }); + }} + data-test-subj="syntheticsBrowserDownloadSpeed" + append={ + + Mbps + + } + /> + + + } + labelAppend={} + isInvalid={!!validate[ConfigKeys.UPLOAD_SPEED]?.(fields)} + error={ + + } + > + + handleInputChange({ + value: event.target.value, + configKey: ConfigKeys.UPLOAD_SPEED, + }) + } + data-test-subj="syntheticsBrowserUploadSpeed" + append={ + + Mbps + + } + /> + + + } + labelAppend={} + isInvalid={!!validate[ConfigKeys.LATENCY]?.(fields)} + data-test-subj="syntheticsBrowserLatency" + error={ + + } + > + + handleInputChange({ + value: event.target.value, + configKey: ConfigKeys.LATENCY, + }) + } + append={ + + ms + + } + /> + + + ) : null; + + return ( + + + + } + description={ + + } + > + + } + onChange={(event) => + handleInputChange({ + value: event.target.checked, + configKey: ConfigKeys.IS_THROTTLING_ENABLED, + }) + } + /> + {throttlingInputs} + + ); +}); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx index bc766462f18aed..7dd279b1767618 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx @@ -25,6 +25,11 @@ export const initialValues: IBrowserAdvancedFields = { [ConfigKeys.JOURNEY_FILTERS_MATCH]: '', [ConfigKeys.JOURNEY_FILTERS_TAGS]: [], [ConfigKeys.IGNORE_HTTPS_ERRORS]: false, + [ConfigKeys.IS_THROTTLING_ENABLED]: true, + [ConfigKeys.DOWNLOAD_SPEED]: '5', + [ConfigKeys.UPLOAD_SPEED]: '3', + [ConfigKeys.LATENCY]: '20', + [ConfigKeys.THROTTLING_CONFIG]: '5d/3u/20l', }; const defaultContext: IBrowserAdvancedFieldsContext = { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx index 805b4aa0d7e0d5..c70424f7910f4d 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx @@ -181,7 +181,7 @@ describe('', () => { }); it('handles switching monitor type', () => { - const { getByText, getByLabelText, queryByLabelText, getAllByLabelText } = render( + const { getByText, queryByText, getByLabelText, queryByLabelText, getAllByLabelText } = render( ); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; @@ -200,7 +200,11 @@ describe('', () => { expect(queryByLabelText('Max redirects')).not.toBeInTheDocument(); // expect tls options to be available for TCP - expect(queryByLabelText('Enable TLS configuration')).toBeInTheDocument(); + // here we must getByText because EUI will generate duplicate aria-labelledby + // values within the test-env generator used, and that will conflict with other + // automatically generated labels. See: + // https://github.com/elastic/eui/blob/91b416dcd51e116edb2cb4a2cac4c306512e28c7/src/services/accessibility/html_id_generator.testenv.ts#L12 + expect(queryByText(/Enable TLS configuration/)).toBeInTheDocument(); // ensure at least one tcp advanced option is present let advancedOptionsButton = getByText('Advanced TCP options'); @@ -214,8 +218,8 @@ describe('', () => { // expect ICMP fields to be in the DOM expect(getByLabelText('Wait in seconds')).toBeInTheDocument(); - // expect tls options not be available for ICMP - expect(queryByLabelText('Enable TLS configuration')).not.toBeInTheDocument(); + // expect tls options not to be available for ICMP + expect(queryByText(/Enable TLS configuration/)).not.toBeInTheDocument(); // expect TCP fields not to be in the DOM expect(queryByLabelText('Proxy URL')).not.toBeInTheDocument(); @@ -234,7 +238,7 @@ describe('', () => { // expect tls options to be available for browser expect(queryByLabelText('Proxy Zip URL')).toBeInTheDocument(); - expect(queryByLabelText('Enable TLS configuration for Zip URL')).toBeInTheDocument(); + expect(queryByText(/Enable TLS configuration for Zip URL/)).toBeInTheDocument(); // ensure at least one browser advanced option is present advancedOptionsButton = getByText('Advanced Browser options'); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx index 1952d50cdd92a8..e136ccf58a3406 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx @@ -195,7 +195,7 @@ export const CustomFields = memo(({ validate, dataStreams = [], children {isHTTP && } {isTCP && } - {isBrowser && } + {isBrowser && } ); }); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx index dc46a4b57bcd8c..40cfdfeba49a5a 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx @@ -308,6 +308,22 @@ describe('useBarChartsHooks', () => { tags: { type: 'yaml', }, + 'throttling.download_speed': { + type: 'text', + value: '""', + }, + 'throttling.upload_speed': { + type: 'text', + value: '""', + }, + 'throttling.latency': { + type: 'text', + value: '""', + }, + 'throttling.config': { + type: 'text', + value: '""', + }, }, }, ], @@ -599,6 +615,7 @@ describe('useBarChartsHooks', () => { validate, monitorType: DataStream.BROWSER, }; + const { result, rerender, waitFor } = renderHook((props) => useUpdatePolicy(props), { initialProps, }); @@ -619,6 +636,9 @@ describe('useBarChartsHooks', () => { [ConfigKeys.SOURCE_ZIP_PASSWORD]: 'password', [ConfigKeys.SCREENSHOTS]: 'off', [ConfigKeys.SYNTHETICS_ARGS]: ['args'], + [ConfigKeys.DOWNLOAD_SPEED]: '13', + [ConfigKeys.UPLOAD_SPEED]: '3', + [ConfigKeys.LATENCY]: '7', }; rerender({ @@ -650,6 +670,11 @@ describe('useBarChartsHooks', () => { config[ConfigKeys.APM_SERVICE_NAME] ); expect(vars?.[ConfigKeys.TIMEOUT].value).toEqual(`${config[ConfigKeys.TIMEOUT]}s`); + expect(vars?.[ConfigKeys.THROTTLING_CONFIG].value).toEqual( + `${config[ConfigKeys.DOWNLOAD_SPEED]}d/${config[ConfigKeys.UPLOAD_SPEED]}u/${ + config[ConfigKeys.LATENCY] + }l` + ); expect(onChange).toBeCalledWith({ isValid: false, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx index 89609efb7ed7f3..5e664aa3103146 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx @@ -72,6 +72,12 @@ export enum ScreenshotOption { ONLY_ON_FAILURE = 'only-on-failure', } +export enum ThrottlingSuffix { + DOWNLOAD = 'd', + UPLOAD = 'u', + LATENCY = 'l', +} + // values must match keys in the integration package export enum ConfigKeys { APM_SERVICE_NAME = 'service.name', @@ -115,6 +121,11 @@ export enum ConfigKeys { TLS_VERSION = 'ssl.supported_protocols', TAGS = 'tags', TIMEOUT = 'timeout', + THROTTLING_CONFIG = 'throttling.config', + IS_THROTTLING_ENABLED = 'throttling.is_enabled', + DOWNLOAD_SPEED = 'throttling.download_speed', + UPLOAD_SPEED = 'throttling.upload_speed', + LATENCY = 'throttling.latency', URLS = 'urls', USERNAME = 'username', WAIT = 'wait', @@ -217,6 +228,11 @@ export interface IBrowserAdvancedFields { [ConfigKeys.JOURNEY_FILTERS_MATCH]: string; [ConfigKeys.JOURNEY_FILTERS_TAGS]: string[]; [ConfigKeys.IGNORE_HTTPS_ERRORS]: boolean; + [ConfigKeys.IS_THROTTLING_ENABLED]: boolean; + [ConfigKeys.DOWNLOAD_SPEED]: string; + [ConfigKeys.UPLOAD_SPEED]: string; + [ConfigKeys.LATENCY]: string; + [ConfigKeys.THROTTLING_CONFIG]: string; } export type HTTPFields = IHTTPSimpleFields & IHTTPAdvancedFields & ITLSFields; @@ -250,3 +266,14 @@ export const contentTypesToMode = { [ContentType.TEXT]: Mode.PLAINTEXT, [ContentType.XML]: Mode.XML, }; + +export type ThrottlingConfigKey = + | ConfigKeys.DOWNLOAD_SPEED + | ConfigKeys.UPLOAD_SPEED + | ConfigKeys.LATENCY; + +export const configKeyToThrottlingSuffix: Record = { + [ConfigKeys.DOWNLOAD_SPEED]: ThrottlingSuffix.DOWNLOAD, + [ConfigKeys.UPLOAD_SPEED]: ThrottlingSuffix.UPLOAD, + [ConfigKeys.LATENCY]: ThrottlingSuffix.LATENCY, +}; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx b/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx index 0ce5dc6f9f02d9..5191297119440e 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx @@ -113,6 +113,12 @@ const validateICMP: ValidationLibrary = { ...validateCommon, }; +const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) => { + if (speed === undefined || speed === '') return false; + const throttleValue = parseFloat(speed); + return isNaN(throttleValue) || (allowZero ? throttleValue < 0 : throttleValue <= 0); +}; + const validateBrowser: ValidationLibrary = { ...validateCommon, [ConfigKeys.SOURCE_ZIP_URL]: ({ @@ -123,6 +129,11 @@ const validateBrowser: ValidationLibrary = { [ConfigKeys.SOURCE_ZIP_URL]: zipUrl, [ConfigKeys.SOURCE_INLINE]: inlineScript, }) => !zipUrl && !inlineScript, + [ConfigKeys.DOWNLOAD_SPEED]: ({ [ConfigKeys.DOWNLOAD_SPEED]: downloadSpeed }) => + validateThrottleValue(downloadSpeed), + [ConfigKeys.UPLOAD_SPEED]: ({ [ConfigKeys.UPLOAD_SPEED]: uploadSpeed }) => + validateThrottleValue(uploadSpeed), + [ConfigKeys.LATENCY]: ({ [ConfigKeys.LATENCY]: latency }) => validateThrottleValue(latency, true), }; export type ValidateDictionary = Record; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts index e4a84e2bb85b5d..a4af2333333f69 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts @@ -26,6 +26,11 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKeys.ZIP_URL_TLS_KEY]: null, [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: null, [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: null, + [ConfigKeys.IS_THROTTLING_ENABLED]: null, + [ConfigKeys.THROTTLING_CONFIG]: null, + [ConfigKeys.DOWNLOAD_SPEED]: null, + [ConfigKeys.UPLOAD_SPEED]: null, + [ConfigKeys.LATENCY]: null, [ConfigKeys.ZIP_URL_TLS_VERSION]: (fields) => arrayFormatter(fields[ConfigKeys.ZIP_URL_TLS_VERSION]), [ConfigKeys.JOURNEY_FILTERS_MATCH]: null, diff --git a/x-pack/test/functional/apps/uptime/synthetics_integration.ts b/x-pack/test/functional/apps/uptime/synthetics_integration.ts index 8fee215545bbe6..49f5895bd98f80 100644 --- a/x-pack/test/functional/apps/uptime/synthetics_integration.ts +++ b/x-pack/test/functional/apps/uptime/synthetics_integration.ts @@ -631,9 +631,82 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { username: 'username', password: 'password', }); + + const advancedConfig = { + screenshots: 'off', + syntheticsArgs: '-ssBlocks', + isThrottlingEnabled: true, + downloadSpeed: '1337', + uploadSpeed: '1338', + latency: '1339', + }; + + await uptimePage.syntheticsIntegration.createBasicBrowserMonitorDetails(config); + await uptimePage.syntheticsIntegration.configureBrowserAdvancedOptions(advancedConfig); + await uptimePage.syntheticsIntegration.confirmAndSave(); + + await uptimePage.syntheticsIntegration.isPolicyCreatedSuccessfully(); + + const [agentPolicy] = await uptimeService.syntheticsPackage.getAgentPolicyList(); + const agentPolicyId = agentPolicy.id; + const agentFullPolicy = await uptimeService.syntheticsPackage.getFullAgentPolicy( + agentPolicyId + ); + + expect(getSyntheticsPolicy(agentFullPolicy)).to.eql( + generatePolicy({ + agentFullPolicy, + version, + name: monitorName, + monitorType: 'browser', + config: { + screenshots: advancedConfig.screenshots, + schedule: '@every 3m', + timeout: '16s', + tags: [config.tags], + 'service.name': config.apmServiceName, + 'source.zip_url.url': config.zipUrl, + 'source.zip_url.folder': config.folder, + 'source.zip_url.username': config.username, + 'source.zip_url.password': config.password, + params: JSON.parse(config.params), + synthetics_args: [advancedConfig.syntheticsArgs], + 'throttling.is_enabled': advancedConfig.isThrottlingEnabled, + 'throttling.download_speed': advancedConfig.downloadSpeed, + 'throttling.upload_speed': advancedConfig.uploadSpeed, + 'throttling.latency': advancedConfig.latency, + 'throttling.config': `${advancedConfig.downloadSpeed}d/${advancedConfig.uploadSpeed}u/${advancedConfig.latency}l`, + __ui: { + is_tls_enabled: false, + is_zip_url_tls_enabled: false, + script_source: { + file_name: '', + is_generated_script: false, + }, + }, + }, + }) + ); + }); + + it('allows saving disabling throttling', async () => { + // This test ensures that updates made to the Synthetics Policy are carried all the way through + // to the generated Agent Policy that is dispatch down to the Elastic Agent. + const config = generateBrowserConfig({ + zipUrl: 'http://test.zip', + params: JSON.stringify({ url: 'http://localhost:8080' }), + folder: 'folder', + username: 'username', + password: 'password', + }); + const advancedConfig = { screenshots: 'off', syntheticsArgs: '-ssBlocks', + isThrottlingEnabled: false, + downloadSpeed: '1337', + uploadSpeed: '1338', + latency: '1339', }; await uptimePage.syntheticsIntegration.createBasicBrowserMonitorDetails(config); @@ -666,6 +739,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'source.zip_url.password': config.password, params: JSON.parse(config.params), synthetics_args: [advancedConfig.syntheticsArgs], + 'throttling.is_enabled': advancedConfig.isThrottlingEnabled, + 'throttling.download_speed': advancedConfig.downloadSpeed, + 'throttling.upload_speed': advancedConfig.uploadSpeed, + 'throttling.latency': advancedConfig.latency, + 'throttling.config': 'false', __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false, diff --git a/x-pack/test/functional/page_objects/synthetics_integration_page.ts b/x-pack/test/functional/page_objects/synthetics_integration_page.ts index 80c4699f6c2115..ad39b4bb02452b 100644 --- a/x-pack/test/functional/page_objects/synthetics_integration_page.ts +++ b/x-pack/test/functional/page_objects/synthetics_integration_page.ts @@ -114,6 +114,14 @@ export function SyntheticsIntegrationPageProvider({ return accordion; }, + /** + * Finds and returns the enable throttling checkbox + */ + async findThrottleSwitch() { + await this.ensureIsOnPackagePage(); + return await testSubjects.find('syntheticsBrowserIsThrottlingEnabled'); + }, + /** * Finds and returns the enable TLS checkbox */ @@ -425,10 +433,36 @@ export function SyntheticsIntegrationPageProvider({ * @params name {string} the name of the monitor * @params zipUrl {string} the zip url of the synthetics suites */ - async configureBrowserAdvancedOptions({ screenshots, syntheticsArgs }: Record) { + async configureBrowserAdvancedOptions({ + screenshots, + syntheticsArgs, + isThrottlingEnabled, + downloadSpeed, + uploadSpeed, + latency, + }: { + screenshots: string; + syntheticsArgs: string; + isThrottlingEnabled: boolean; + downloadSpeed: string; + uploadSpeed: string; + latency: string; + }) { await testSubjects.click('syntheticsBrowserAdvancedFieldsAccordion'); + + const throttleSwitch = await this.findThrottleSwitch(); + if ((await throttleSwitch.isSelected()) !== isThrottlingEnabled) { + await throttleSwitch.click(); + } + await testSubjects.selectValue('syntheticsBrowserScreenshots', screenshots); await this.setComboBox('syntheticsBrowserSyntheticsArgs', syntheticsArgs); + + if (isThrottlingEnabled) { + await this.fillTextInputByTestSubj('syntheticsBrowserDownloadSpeed', downloadSpeed); + await this.fillTextInputByTestSubj('syntheticsBrowserUploadSpeed', uploadSpeed); + await this.fillTextInputByTestSubj('syntheticsBrowserLatency', latency); + } }, }; } From 4107d401f3df678fcfbecc91481ba48a7fa0c681 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Thu, 2 Dec 2021 15:04:07 +0300 Subject: [PATCH 15/20] [Discover] Close expanded document sidebar when switch data views (#119736) * [Discover] close expanded document sidebar on index pattern change * [Discover] fix unit test * [Discover] fix use_discover_state unit test * [Discover] fix linting * [Discover] apply suggestions * [Discover] fix linting Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/context/context_app_content.tsx | 3 ++- .../components/layout/discover_documents.test.tsx | 2 +- .../main/components/layout/discover_documents.tsx | 5 +++-- .../main/components/layout/discover_layout.test.tsx | 3 ++- .../main/components/layout/discover_layout.tsx | 5 +++-- .../application/main/components/layout/types.ts | 3 +++ .../main/components/sidebar/discover_sidebar.test.tsx | 2 +- .../main/components/sidebar/discover_sidebar.tsx | 2 +- .../sidebar/discover_sidebar_responsive.test.tsx | 2 +- .../main/components/sidebar/lib/get_details.ts | 2 +- .../public/application/main/discover_main_app.tsx | 7 ++++++- .../application/main/utils/calc_field_counts.test.ts | 2 +- .../application/main/utils/calc_field_counts.ts | 2 +- .../application/main/utils/use_discover_state.test.ts | 3 +++ .../application/main/utils/use_discover_state.ts | 6 +++++- .../application/main/utils/use_saved_search.test.ts | 2 ++ .../public/application/main/utils/use_saved_search.ts | 2 +- .../components/discover_grid/discover_grid.test.tsx | 2 +- .../public/components/discover_grid/discover_grid.tsx | 5 +++-- .../discover_grid/discover_grid_context.tsx | 7 ++++--- .../discover_grid_document_selection.tsx | 2 +- .../components/discover_grid/discover_grid_flyout.tsx | 3 ++- .../discover_grid/get_render_cell_value.test.tsx | 2 +- .../discover_grid/get_render_cell_value.tsx | 2 +- .../components/doc_table/components/table_row.tsx | 3 ++- .../public/embeddable/saved_search_embeddable.tsx | 2 +- .../discover/public/embeddable/saved_search_grid.tsx | 2 +- .../components/doc_viewer/doc_viewer_tab.test.tsx | 2 +- .../components/doc_viewer_table/table.test.tsx | 2 +- .../doc_views/components/doc_viewer_table/table.tsx | 3 ++- .../public/services/doc_views/doc_views_registry.ts | 3 ++- .../public/services/doc_views/doc_views_types.ts | 5 +---- src/plugins/discover/public/types.ts | 11 +++++++++++ .../discover/public/utils/use_es_doc_search.ts | 2 +- 34 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 src/plugins/discover/public/types.ts diff --git a/src/plugins/discover/public/application/context/context_app_content.tsx b/src/plugins/discover/public/application/context/context_app_content.tsx index f498c6f0a244d6..45bc3df77bd226 100644 --- a/src/plugins/discover/public/application/context/context_app_content.tsx +++ b/src/plugins/discover/public/application/context/context_app_content.tsx @@ -15,7 +15,7 @@ import { SortDirection } from '../../../../data/public'; import { LoadingStatus } from './services/context_query_state'; import { ActionBar } from './components/action_bar/action_bar'; import { DiscoverGrid } from '../../components/discover_grid/discover_grid'; -import { DocViewFilterFn, ElasticSearchHit } from '../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { AppState } from './services/context_state'; import { SurrDocType } from './services/context'; import { DiscoverServices } from '../../build_services'; @@ -23,6 +23,7 @@ import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './services/constants'; import { DocTableContext } from '../../components/doc_table/doc_table_context'; import { EsHitRecordList } from '../types'; import { SortPairArr } from '../../components/doc_table/lib/get_sort'; +import { ElasticSearchHit } from '../../types'; export interface ContextAppContentProps { columns: string[]; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx index 829d88bbafd082..45c064d06c51fa 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx @@ -17,8 +17,8 @@ import { DataDocuments$ } from '../../utils/use_saved_search'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { FetchStatus } from '../../../types'; import { DiscoverDocuments } from './discover_documents'; -import { ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; +import { ElasticSearchHit } from 'src/plugins/discover/public/types'; jest.mock('../../../../kibana_services', () => ({ ...jest.requireActual('../../../../kibana_services'), diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 154f83796490bc..ae0f5ad670ce93 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -14,7 +14,7 @@ import { EuiScreenReaderOnly, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DocViewFilterFn, ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types'; import { DiscoverGrid } from '../../../../components/discover_grid/discover_grid'; import { FetchStatus } from '../../../types'; import { @@ -32,6 +32,7 @@ import { AppState, GetStateReturn } from '../../services/discover_state'; import { useDataState } from '../../utils/use_data_state'; import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; import { SortPairArr } from '../../../../components/doc_table/lib/get_sort'; +import { ElasticSearchHit } from '../../../../types'; const DocTableInfiniteMemoized = React.memo(DocTableInfinite); const DataGridMemoized = React.memo(DiscoverGrid); @@ -54,7 +55,7 @@ function DiscoverDocumentsComponent({ onAddFilter: DocViewFilterFn; savedSearch: SavedSearch; services: DiscoverServices; - setExpandedDoc: (doc: ElasticSearchHit | undefined) => void; + setExpandedDoc: (doc?: ElasticSearchHit) => void; state: AppState; stateContainer: GetStateReturn; }) { diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index c222e4038f5170..77eeb88904100b 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -28,10 +28,10 @@ import { } from '../../utils/use_saved_search'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { FetchStatus } from '../../../types'; -import { ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; import { RequestAdapter } from '../../../../../../inspector'; import { Chart } from '../chart/point_series'; import { DiscoverSidebar } from '../sidebar/discover_sidebar'; +import { ElasticSearchHit } from '../../../../types'; jest.mock('../../../../kibana_services', () => ({ ...jest.requireActual('../../../../kibana_services'), @@ -150,6 +150,7 @@ function getProps(indexPattern: IndexPattern, wasSidebarClosed?: boolean): Disco services, state: { columns: [] }, stateContainer: {} as GetStateReturn, + setExpandedDoc: jest.fn(), }; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 39d8268de2d0b3..4b647867cf64a7 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -29,7 +29,7 @@ import { DiscoverLayoutProps } from './types'; import { SEARCH_FIELDS_FROM_SOURCE, SHOW_FIELD_STATISTICS } from '../../../../../common'; import { popularizeField } from '../../../../utils/popularize_field'; import { DiscoverTopNav } from '../top_nav/discover_topnav'; -import { DocViewFilterFn, ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types'; import { DiscoverChart } from '../chart'; import { getResultState } from '../../utils/get_result_state'; import { InspectorSession } from '../../../../../../inspector/public'; @@ -62,9 +62,11 @@ export function DiscoverLayout({ indexPattern, indexPatternList, inspectorAdapters, + expandedDoc, navigateTo, onChangeIndexPattern, onUpdateQuery, + setExpandedDoc, savedSearchRefetch$, resetSavedSearch, savedSearchData$, @@ -86,7 +88,6 @@ export function DiscoverLayout({ spaces, } = services; const { main$, charts$, totalHits$ } = savedSearchData$; - const [expandedDoc, setExpandedDoc] = useState(undefined); const [inspectorSession, setInspectorSession] = useState(undefined); const viewMode = useMemo(() => { diff --git a/src/plugins/discover/public/application/main/components/layout/types.ts b/src/plugins/discover/public/application/main/components/layout/types.ts index 1ed34978416fac..7d0f747b9bd5a4 100644 --- a/src/plugins/discover/public/application/main/components/layout/types.ts +++ b/src/plugins/discover/public/application/main/components/layout/types.ts @@ -19,6 +19,7 @@ import { DataRefetch$, SavedSearchData } from '../../utils/use_saved_search'; import { DiscoverServices } from '../../../../build_services'; import { SavedSearch } from '../../../../services/saved_searches'; import { RequestAdapter } from '../../../../../../inspector'; +import { ElasticSearchHit } from '../../../../types'; export interface DiscoverLayoutProps { indexPattern: IndexPattern; @@ -28,6 +29,8 @@ export interface DiscoverLayoutProps { onChangeIndexPattern: (id: string) => void; onUpdateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; resetSavedSearch: () => void; + expandedDoc?: ElasticSearchHit; + setExpandedDoc: (doc?: ElasticSearchHit) => void; savedSearch: SavedSearch; savedSearchData$: SavedSearchData; savedSearchRefetch$: DataRefetch$; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx index 9dd7ef19ffc072..a4d7107a7f15cb 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx @@ -19,10 +19,10 @@ import { flattenHit, IndexPatternAttributes } from '../../../../../../data/commo import { SavedObject } from '../../../../../../../core/types'; import { getDefaultFieldFilter } from './lib/field_filter'; import { DiscoverSidebarComponent as DiscoverSidebar } from './discover_sidebar'; -import { ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; import { discoverServiceMock as mockDiscoverServices } from '../../../../__mocks__/services'; import { stubLogstashIndexPattern } from '../../../../../../data/common/stubs'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; +import { ElasticSearchHit } from '../../../../types'; jest.mock('../../../../kibana_services', () => ({ getServices: () => mockDiscoverServices, diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index 3b4ff997f324ea..78aee49d1b288b 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -39,8 +39,8 @@ import { FieldFilterState, getDefaultFieldFilter, setFieldFilterProp } from './l import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; import { DiscoverSidebarResponsiveProps } from './discover_sidebar_responsive'; import { DiscoverIndexPatternManagement } from './discover_index_pattern_management'; -import { ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; +import { ElasticSearchHit } from '../../../../types'; /** * Default number of available fields displayed and added on scroll diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index b412cd69c82af2..c65d0b0a4ec2cd 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -22,11 +22,11 @@ import { DiscoverSidebarResponsiveProps, } from './discover_sidebar_responsive'; import { DiscoverServices } from '../../../../build_services'; -import { ElasticSearchHit } from '../../../../services/doc_views/doc_views_types'; import { FetchStatus } from '../../../types'; import { DataDocuments$ } from '../../utils/use_saved_search'; import { stubLogstashIndexPattern } from '../../../../../../data/common/stubs'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; +import { ElasticSearchHit } from '../../../../types'; const mockServices = { history: () => ({ diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_details.ts index b5beebf6fb8d46..0715c7377da7d5 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_details.ts @@ -9,7 +9,7 @@ // @ts-expect-error import { fieldCalculator } from './field_calculator'; import { IndexPattern, IndexPatternField } from '../../../../../../../data/public'; -import { ElasticSearchHit } from '../../../../../services/doc_views/doc_views_types'; +import { ElasticSearchHit } from '../../../../../types'; export function getDetails( field: IndexPatternField, diff --git a/src/plugins/discover/public/application/main/discover_main_app.tsx b/src/plugins/discover/public/application/main/discover_main_app.tsx index ea3f852a5290ab..1ef6641e9bc727 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { History } from 'history'; import { DiscoverLayout } from './components/layout'; import { setBreadcrumbsTitle } from '../../utils/breadcrumbs'; @@ -15,6 +15,7 @@ import { useUrl } from './utils/use_url'; import { IndexPatternAttributes, SavedObject } from '../../../../data/common'; import { DiscoverServices } from '../../build_services'; import { SavedSearch } from '../../services/saved_searches'; +import { ElasticSearchHit } from '../../types'; const DiscoverLayoutMemoized = React.memo(DiscoverLayout); @@ -40,6 +41,7 @@ export interface DiscoverMainProps { export function DiscoverMainApp(props: DiscoverMainProps) { const { savedSearch, services, history, indexPatternList } = props; const { chrome, docLinks, uiSettings: config, data } = services; + const [expandedDoc, setExpandedDoc] = useState(undefined); const navigateTo = useCallback( (path: string) => { history.push(path); @@ -65,6 +67,7 @@ export function DiscoverMainApp(props: DiscoverMainProps) { services, history, savedSearch, + setExpandedDoc, }); /** @@ -100,9 +103,11 @@ export function DiscoverMainApp(props: DiscoverMainProps) { indexPattern={indexPattern} indexPatternList={indexPatternList} inspectorAdapters={inspectorAdapters} + expandedDoc={expandedDoc} onChangeIndexPattern={onChangeIndexPattern} onUpdateQuery={onUpdateQuery} resetSavedSearch={resetCurrentSavedSearch} + setExpandedDoc={setExpandedDoc} navigateTo={navigateTo} savedSearch={savedSearch} savedSearchData$={data$} diff --git a/src/plugins/discover/public/application/main/utils/calc_field_counts.test.ts b/src/plugins/discover/public/application/main/utils/calc_field_counts.test.ts index 9d198947e06c7c..28d814b871104e 100644 --- a/src/plugins/discover/public/application/main/utils/calc_field_counts.test.ts +++ b/src/plugins/discover/public/application/main/utils/calc_field_counts.test.ts @@ -8,7 +8,7 @@ import { calcFieldCounts } from './calc_field_counts'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; -import { ElasticSearchHit } from '../../../services/doc_views/doc_views_types'; +import { ElasticSearchHit } from '../../../types'; describe('calcFieldCounts', () => { test('returns valid field count data', async () => { diff --git a/src/plugins/discover/public/application/main/utils/calc_field_counts.ts b/src/plugins/discover/public/application/main/utils/calc_field_counts.ts index 08d1a2639fa0b4..9a7f68550d8407 100644 --- a/src/plugins/discover/public/application/main/utils/calc_field_counts.ts +++ b/src/plugins/discover/public/application/main/utils/calc_field_counts.ts @@ -7,7 +7,7 @@ */ import { flattenHit, IndexPattern } from '../../../../../data/common'; -import { ElasticSearchHit } from '../../../services/doc_views/doc_views_types'; +import { ElasticSearchHit } from '../../../types'; /** * This function is recording stats of the available fields, for usage in sidebar and sharing diff --git a/src/plugins/discover/public/application/main/utils/use_discover_state.test.ts b/src/plugins/discover/public/application/main/utils/use_discover_state.test.ts index 78f742b9f7c9b9..bac6d085acf05b 100644 --- a/src/plugins/discover/public/application/main/utils/use_discover_state.test.ts +++ b/src/plugins/discover/public/application/main/utils/use_discover_state.test.ts @@ -37,6 +37,7 @@ describe('test useDiscoverState', () => { services: discoverServiceMock, history, savedSearch: savedSearchMock, + setExpandedDoc: jest.fn(), }); }); expect(result.current.state.index).toBe(indexPatternMock.id); @@ -53,6 +54,7 @@ describe('test useDiscoverState', () => { services: discoverServiceMock, history, savedSearch: savedSearchMock, + setExpandedDoc: jest.fn(), }); }); await act(async () => { @@ -69,6 +71,7 @@ describe('test useDiscoverState', () => { services: discoverServiceMock, history, savedSearch: savedSearchMock, + setExpandedDoc: jest.fn(), }); }); diff --git a/src/plugins/discover/public/application/main/utils/use_discover_state.ts b/src/plugins/discover/public/application/main/utils/use_discover_state.ts index b70bcded4c6086..a4ef9382b4995a 100644 --- a/src/plugins/discover/public/application/main/utils/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/utils/use_discover_state.ts @@ -24,15 +24,18 @@ import { useSearchSession } from './use_search_session'; import { FetchStatus } from '../../types'; import { getSwitchIndexPatternAppState } from './get_switch_index_pattern_app_state'; import { SortPairArr } from '../../../components/doc_table/lib/get_sort'; +import { ElasticSearchHit } from '../../../types'; export function useDiscoverState({ services, history, savedSearch, + setExpandedDoc, }: { services: DiscoverServices; savedSearch: SavedSearch; history: History; + setExpandedDoc: (doc?: ElasticSearchHit) => void; }) { const { uiSettings: config, data, filterManager, indexPatterns, storage } = services; const useNewFieldsApi = useMemo(() => !config.get(SEARCH_FIELDS_FROM_SOURCE), [config]); @@ -186,8 +189,9 @@ export function useDiscoverState({ ); stateContainer.setAppState(nextAppState); } + setExpandedDoc(undefined); }, - [config, indexPattern, indexPatterns, state.columns, state.sort, stateContainer] + [config, indexPattern, indexPatterns, setExpandedDoc, state.columns, state.sort, stateContainer] ); /** * Function triggered when the user changes the query in the search bar diff --git a/src/plugins/discover/public/application/main/utils/use_saved_search.test.ts b/src/plugins/discover/public/application/main/utils/use_saved_search.test.ts index b3ed7ab8541902..2788d63fdf9839 100644 --- a/src/plugins/discover/public/application/main/utils/use_saved_search.test.ts +++ b/src/plugins/discover/public/application/main/utils/use_saved_search.test.ts @@ -59,6 +59,7 @@ describe('test useSavedSearch', () => { services: discoverServiceMock, history, savedSearch: savedSearchMock, + setExpandedDoc: jest.fn(), }); }); @@ -100,6 +101,7 @@ describe('test useSavedSearch', () => { services: discoverServiceMock, history, savedSearch: savedSearchMock, + setExpandedDoc: jest.fn(), }); }); diff --git a/src/plugins/discover/public/application/main/utils/use_saved_search.ts b/src/plugins/discover/public/application/main/utils/use_saved_search.ts index bfd6f1daa4bc00..0f4b9058316a02 100644 --- a/src/plugins/discover/public/application/main/utils/use_saved_search.ts +++ b/src/plugins/discover/public/application/main/utils/use_saved_search.ts @@ -11,7 +11,6 @@ import { DiscoverServices } from '../../../build_services'; import { DiscoverSearchSessionManager } from '../services/discover_search_session'; import { ISearchSource } from '../../../../../data/common'; import { GetStateReturn } from '../services/discover_state'; -import { ElasticSearchHit } from '../../../services/doc_views/doc_views_types'; import { RequestAdapter } from '../../../../../inspector/public'; import type { AutoRefreshDoneFn } from '../../../../../data/public'; import { validateTimeRange } from './validate_time_range'; @@ -23,6 +22,7 @@ import { fetchAll } from './fetch_all'; import { useBehaviorSubject } from './use_behavior_subject'; import { sendResetMsg } from './use_saved_search_messages'; import { getFetch$ } from './get_fetch_observable'; +import { ElasticSearchHit } from '../../../types'; export interface SavedSearchData { main$: DataMain$; diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx index 4a0e472f17455c..d1a1f2dcdbe8af 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx @@ -16,8 +16,8 @@ import { mountWithIntl } from '@kbn/test/jest'; import { DiscoverGrid, DiscoverGridProps } from './discover_grid'; import { uiSettingsMock } from '../../__mocks__/ui_settings'; import { DiscoverServices } from '../../build_services'; -import { ElasticSearchHit } from '../../services/doc_views/doc_views_types'; import { getDocId } from './discover_grid_document_selection'; +import { ElasticSearchHit } from '../../types'; jest.mock('../../kibana_services', () => ({ ...jest.requireActual('../../kibana_services'), diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index fa83a7527de120..c9d2ec72bfc026 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -22,7 +22,7 @@ import { EuiIcon, } from '@elastic/eui'; import { flattenHit, IndexPattern } from '../../../../data/common'; -import { DocViewFilterFn, ElasticSearchHit } from '../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { getSchemaDetectors } from './discover_grid_schema'; import { DiscoverGridFlyout } from './discover_grid_flyout'; import { DiscoverGridContext } from './discover_grid_context'; @@ -49,6 +49,7 @@ import { import { DiscoverGridDocumentToolbarBtn, getDocId } from './discover_grid_document_selection'; import { SortPairArr } from '../doc_table/lib/get_sort'; import { getFieldsToShow } from '../../utils/get_fields_to_show'; +import { ElasticSearchHit } from '../../types'; interface SortObj { id: string; @@ -116,7 +117,7 @@ export interface DiscoverGridProps { /** * Function to set the expanded document, which is displayed in a flyout */ - setExpandedDoc: (doc: ElasticSearchHit | undefined) => void; + setExpandedDoc: (doc?: ElasticSearchHit) => void; /** * Grid display settings persisted in Elasticsearch (e.g. column width) */ diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx index 49b72ef126a76a..3bf80872b06859 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx @@ -8,11 +8,12 @@ import React from 'react'; import type { IndexPattern } from 'src/plugins/data/common'; -import { DocViewFilterFn, ElasticSearchHit } from '../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; +import { ElasticSearchHit } from '../../types'; export interface GridContext { - expanded: ElasticSearchHit | undefined; - setExpanded: (hit: ElasticSearchHit | undefined) => void; + expanded?: ElasticSearchHit; + setExpanded: (hit?: ElasticSearchHit) => void; rows: ElasticSearchHit[]; onFilter: DocViewFilterFn; indexPattern: IndexPattern; diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx index c517d3a65b6b9f..6fb614327d2af4 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx @@ -21,8 +21,8 @@ import { euiLightVars as themeLight, euiDarkVars as themeDark, } from '@kbn/ui-shared-deps-src/theme'; -import { ElasticSearchHit } from '../../services/doc_views/doc_views_types'; import { DiscoverGridContext } from './discover_grid_context'; +import { ElasticSearchHit } from '../../types'; /** * Returning a generated id of a given ES document, since `_id` can be the same diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx index d5b2248162b2fa..30e0cf24f7d528 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx @@ -25,10 +25,11 @@ import { keys, } from '@elastic/eui'; import { DocViewer } from '../../services/doc_views/components/doc_viewer/doc_viewer'; -import { DocViewFilterFn, ElasticSearchHit } from '../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { DiscoverServices } from '../../build_services'; import { getContextUrl } from '../../utils/get_context_url'; import { getSingleDocUrl } from '../../utils/get_single_doc_url'; +import { ElasticSearchHit } from '../../types'; interface Props { columns: string[]; diff --git a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx index e97a2b2901f332..b81dda5c279152 100644 --- a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { ReactWrapper, shallow } from 'enzyme'; import { getRenderCellValueFn } from './get_render_cell_value'; import { indexPatternMock } from '../../__mocks__/index_pattern'; -import { ElasticSearchHit } from '../../services/doc_views/doc_views_types'; import { flattenHit } from 'src/plugins/data/common'; +import { ElasticSearchHit } from '../../types'; jest.mock('../../../../kibana_react/public', () => ({ useUiSetting: () => true, diff --git a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.tsx index 8fd5f73701932f..c1673dad7eaa7f 100644 --- a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.tsx +++ b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.tsx @@ -19,13 +19,13 @@ import { EuiDescriptionListTitle, EuiDescriptionListDescription, } from '@elastic/eui'; -import { ElasticSearchHit } from '../../services/doc_views/doc_views_types'; import { DiscoverGridContext } from './discover_grid_context'; import { JsonCodeEditor } from '../json_code_editor/json_code_editor'; import { defaultMonacoEditorWidth } from './constants'; import { EsHitRecord } from '../../application/types'; import { formatFieldValue } from '../../utils/format_value'; import { formatHit } from '../../utils/format_hit'; +import { ElasticSearchHit } from '../../types'; export const getRenderCellValueFn = ( diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.tsx index 8a980cc4160d8a..2eee9a177e4f8b 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row.tsx @@ -15,11 +15,12 @@ import { flattenHit } from '../../../../../data/common'; import { DocViewer } from '../../../services/doc_views/components/doc_viewer/doc_viewer'; import { FilterManager, IndexPattern } from '../../../../../data/public'; import { TableCell } from './table_row/table_cell'; -import { ElasticSearchHit, DocViewFilterFn } from '../../../services/doc_views/doc_views_types'; +import { DocViewFilterFn } from '../../../services/doc_views/doc_views_types'; import { getContextUrl } from '../../../utils/get_context_url'; import { getSingleDocUrl } from '../../../utils/get_single_doc_url'; import { TableRowDetails } from './table_row_details'; import { formatRow, formatTopLevelObject } from '../lib/row_formatter'; +import { ElasticSearchHit } from '../../../types'; export type DocTableRow = ElasticSearchHit & { isAnchor?: boolean; diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index 7146f84e194e2c..4a7f0b1c36868d 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -26,7 +26,6 @@ import { Query, TimeRange, } from '../../../data/common'; -import { ElasticSearchHit } from '../services/doc_views/doc_views_types'; import { SavedSearchEmbeddableComponent } from './saved_search_embeddable_component'; import { UiActionsStart } from '../../../ui_actions/public'; import { getServices } from '../kibana_services'; @@ -48,6 +47,7 @@ import { SortOrder } from '../components/doc_table/components/table_header/helpe import { VIEW_MODE } from '../components/view_mode_toggle'; import { updateSearchSource } from './utils/update_search_source'; import { FieldStatsTableSavedSearchEmbeddable } from '../application/main/components/field_stats_table'; +import { ElasticSearchHit } from '../types'; export type SearchProps = Partial & Partial & { diff --git a/src/plugins/discover/public/embeddable/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/saved_search_grid.tsx index a28e509d3ba725..aa15ccb79da8b8 100644 --- a/src/plugins/discover/public/embeddable/saved_search_grid.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_grid.tsx @@ -10,8 +10,8 @@ import { I18nProvider } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DiscoverGrid, DiscoverGridProps } from '../components/discover_grid/discover_grid'; import { getServices } from '../kibana_services'; -import { ElasticSearchHit } from '../services/doc_views/doc_views_types'; import { TotalDocuments } from '../application/main/components/total_documents/total_documents'; +import { ElasticSearchHit } from '../types'; export interface DiscoverGridEmbeddableProps extends DiscoverGridProps { totalHitCount: number; diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer/doc_viewer_tab.test.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer/doc_viewer_tab.test.tsx index 3537699e4fe20c..8eab6b1e12c8cb 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer/doc_viewer_tab.test.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer/doc_viewer_tab.test.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DocViewerTab } from './doc_viewer_tab'; -import { ElasticSearchHit } from '../../doc_views_types'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; +import { ElasticSearchHit } from 'src/plugins/discover/public/types'; describe('DocViewerTab', () => { test('changing columns triggers an update', () => { diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.test.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.test.tsx index 5133ca46015a0d..c956b36b625947 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.test.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.test.tsx @@ -11,13 +11,13 @@ import { mountWithIntl } from '@kbn/test/jest'; import { findTestSubject } from '@elastic/eui/lib/test'; import { DocViewerTable, DocViewerTableProps } from './table'; import { IndexPattern } from '../../../../../../data/public'; -import { ElasticSearchHit } from '../../doc_views_types'; jest.mock('../../../../kibana_services', () => ({ getServices: jest.fn(), })); import { getServices } from '../../../../kibana_services'; +import { ElasticSearchHit } from '../../../../types'; (getServices as jest.Mock).mockImplementation(() => ({ uiSettings: { diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx index 707a98d87c9240..433227133370ef 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table.tsx @@ -13,12 +13,13 @@ import { IndexPattern, IndexPatternField } from '../../../../../../data/public'; import { flattenHit } from '../../../../../../data/common'; import { SHOW_MULTIFIELDS } from '../../../../../common'; import { getServices } from '../../../../kibana_services'; -import { DocViewFilterFn, ElasticSearchHit, DocViewRenderProps } from '../../doc_views_types'; +import { DocViewFilterFn, DocViewRenderProps } from '../../doc_views_types'; import { ACTIONS_COLUMN, MAIN_COLUMNS } from './table_columns'; import { getFieldsToShow } from '../../../../utils/get_fields_to_show'; import { getIgnoredReason, IgnoredReason } from '../../../../utils/get_ignored_reason'; import { formatFieldValue } from '../../../../utils/format_value'; import { isNestedFieldParent } from '../../../../application/main/utils/nested_fields'; +import { ElasticSearchHit } from '../../../../types'; export interface DocViewerTableProps { columns?: string[]; diff --git a/src/plugins/discover/public/services/doc_views/doc_views_registry.ts b/src/plugins/discover/public/services/doc_views/doc_views_registry.ts index 26b5016881b852..8ee8741d73d3f5 100644 --- a/src/plugins/discover/public/services/doc_views/doc_views_registry.ts +++ b/src/plugins/discover/public/services/doc_views/doc_views_registry.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { DocView, DocViewInput, ElasticSearchHit, DocViewInputFn } from './doc_views_types'; +import { ElasticSearchHit } from '../../types'; +import { DocView, DocViewInput, DocViewInputFn } from './doc_views_types'; export class DocViewsRegistry { private docViews: DocView[] = []; diff --git a/src/plugins/discover/public/services/doc_views/doc_views_types.ts b/src/plugins/discover/public/services/doc_views/doc_views_types.ts index e8faa51bbab40d..ef2600b4d04df5 100644 --- a/src/plugins/discover/public/services/doc_views/doc_views_types.ts +++ b/src/plugins/discover/public/services/doc_views/doc_views_types.ts @@ -7,11 +7,8 @@ */ import { ComponentType } from 'react'; - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IndexPattern } from '../../../../data/public'; - -export type ElasticSearchHit = estypes.SearchHit; +import { ElasticSearchHit } from '../../types'; export interface FieldMapping { filterable?: boolean; diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts new file mode 100644 index 00000000000000..f6872e9951c378 --- /dev/null +++ b/src/plugins/discover/public/types.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +export type ElasticSearchHit = estypes.SearchHit; diff --git a/src/plugins/discover/public/utils/use_es_doc_search.ts b/src/plugins/discover/public/utils/use_es_doc_search.ts index 10c97c53b3fb31..c8b870b1a1daef 100644 --- a/src/plugins/discover/public/utils/use_es_doc_search.ts +++ b/src/plugins/discover/public/utils/use_es_doc_search.ts @@ -11,9 +11,9 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IndexPattern } from '../../../data/common'; import { DocProps } from '../application/doc/components/doc'; import { ElasticRequestState } from '../application/doc/types'; -import { ElasticSearchHit } from '../services/doc_views/doc_views_types'; import { getServices } from '../kibana_services'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../common'; +import { ElasticSearchHit } from '../types'; type RequestBody = Pick; From 42e2e782532dbbf27bbb942b964e43ce65450f62 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:05:13 +0100 Subject: [PATCH 16/20] [Fleet] Update calculateAuthz calls to respect superuser for now (#119973) --- .../fleet/public/mock/plugin_interfaces.ts | 4 +- x-pack/plugins/fleet/public/mock/types.ts | 3 +- x-pack/plugins/fleet/public/plugin.ts | 87 ++++++++++--------- .../plugins/fleet/server/routes/security.ts | 62 +++++++------ 4 files changed, 81 insertions(+), 75 deletions(-) diff --git a/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts b/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts index 054ef958c19144..2373f8dd309ba8 100644 --- a/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts +++ b/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts @@ -14,7 +14,7 @@ export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): Mo return { isInitialized: jest.fn().mockResolvedValue(true), registerExtension: createExtensionRegistrationCallback(extensionsStorage), - authz: { + authz: Promise.resolve({ fleet: { all: true, setup: true, @@ -31,6 +31,6 @@ export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): Mo readIntegrationPolicies: true, writeIntegrationPolicies: true, }, - }, + }), }; }; diff --git a/x-pack/plugins/fleet/public/mock/types.ts b/x-pack/plugins/fleet/public/mock/types.ts index 5071eb71fece1c..44d88acae1617c 100644 --- a/x-pack/plugins/fleet/public/mock/types.ts +++ b/x-pack/plugins/fleet/public/mock/types.ts @@ -15,4 +15,5 @@ export type MockedFleetSetupDeps = MockedKeys; export type MockedFleetStartDeps = MockedKeys; -export type MockedFleetStart = MockedKeys; +// Don't wrap the `authz` property which is a promise with `jest.Mocked` +export type MockedFleetStart = MockedKeys> & Pick; diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index e188b8e99b5b99..2330cd3690c775 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -24,6 +24,8 @@ import type { import type { SharePluginStart } from 'src/plugins/share/public'; +import { once } from 'lodash'; + import type { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; import { DEFAULT_APP_CATEGORIES, AppNavLinkStatus } from '../../../../src/core/public'; @@ -72,7 +74,7 @@ export interface FleetSetup {} */ export interface FleetStart { /** Authorization for the current user */ - authz: FleetAuthz; + authz: Promise; registerExtension: UIExtensionRegistrationCallback; isInitialized: () => Promise; } @@ -144,7 +146,7 @@ export class FleetPlugin implements Plugin; const registerExtension = createExtensionRegistrationCallback(this.extensions); + const getPermissions = once(() => + core.http.get(appRoutesService.getCheckPermissionsPath()) + ); registerExtension({ package: CUSTOM_LOGS_INTEGRATION_NAME, @@ -246,46 +250,49 @@ export class FleetPlugin implements Plugin { + // eslint-disable-next-line no-console + console.warn(`Could not load Fleet permissions due to error: ${e}`); + return { success: false }; + }) + .then((permissionsResponse) => { + if (permissionsResponse.success) { + // If superuser, give access to everything + return calculateAuthz({ + fleet: { all: true, setup: true }, + integrations: { all: true, read: true }, + }); + } else { + // All other users only get access to read integrations if they have the read privilege + const { capabilities } = core.application; + return calculateAuthz({ + fleet: { all: false, setup: false }, + integrations: { all: false, read: capabilities.fleet.read as boolean }, + }); + } + }), - integrations: { - all: capabilities.fleet.all as boolean, - read: capabilities.fleet.read as boolean, - }, - }); + isInitialized: once(async () => { + const permissionsResponse = await getPermissions(); - return { - authz, - isInitialized: () => { - if (!successPromise) { - successPromise = Promise.resolve().then(async () => { - const permissionsResponse = await core.http.get( - appRoutesService.getCheckPermissionsPath() - ); - - if (permissionsResponse?.success) { - return core.http - .post(setupRouteService.getSetupPath()) - .then(({ isInitialized }) => - isInitialized - ? Promise.resolve(true) - : Promise.reject(new Error('Unknown setup error')) - ); - } else { - throw new Error(permissionsResponse?.error || 'Unknown permissions error'); - } - }); + if (permissionsResponse?.success) { + const { isInitialized } = await core.http.post( + setupRouteService.getSetupPath() + ); + if (!isInitialized) { + throw new Error('Unknown setup error'); + } + + return true; + } else { + throw new Error(permissionsResponse?.error || 'Unknown permissions error'); } + }), - return successPromise; - }, registerExtension, }; } diff --git a/x-pack/plugins/fleet/server/routes/security.ts b/x-pack/plugins/fleet/server/routes/security.ts index 8e037c25ceca92..9853877dc2d61b 100644 --- a/x-pack/plugins/fleet/server/routes/security.ts +++ b/x-pack/plugins/fleet/server/routes/security.ts @@ -127,41 +127,39 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise Date: Thu, 2 Dec 2021 08:30:44 -0500 Subject: [PATCH 17/20] [Fleet] Use predefined id for default output (#120158) --- x-pack/plugins/fleet/common/constants/output.ts | 4 ++-- x-pack/plugins/fleet/server/constants/index.ts | 1 + x-pack/plugins/fleet/server/services/output.ts | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index c750be12be2dfe..67c45fea37d78d 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -13,10 +13,10 @@ export const outputType = { Elasticsearch: 'elasticsearch', } as const; -export const DEFAULT_OUTPUT_ID = 'default'; +export const DEFAULT_OUTPUT_ID = 'fleet-default-output'; export const DEFAULT_OUTPUT: NewOutput = { - name: DEFAULT_OUTPUT_ID, + name: 'default', is_default: true, is_default_monitoring: true, type: outputType.Elasticsearch, diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index 633390c368957f..0769f02fc0272b 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -46,6 +46,7 @@ export { DEFAULT_AGENT_POLICY, DEFAULT_FLEET_SERVER_AGENT_POLICY, DEFAULT_OUTPUT, + DEFAULT_OUTPUT_ID, DEFAULT_PACKAGES, PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, AGENT_POLICY_DEFAULT_MONITORING_DATASETS, diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 511aba4e6a9329..61592420e91160 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -9,7 +9,7 @@ import type { SavedObject, SavedObjectsClientContract } from 'src/core/server'; import uuid from 'uuid/v5'; import type { NewOutput, Output, OutputSOAttributes } from '../types'; -import { DEFAULT_OUTPUT, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; +import { DEFAULT_OUTPUT, DEFAULT_OUTPUT_ID, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; import { decodeCloudId, normalizeHostsForAgents, SO_SEARCH_LIMIT } from '../../common'; import { OutputUnauthorizedError } from '../errors'; @@ -75,7 +75,10 @@ class OutputService { is_default_monitoring: !defaultMonitoringOutput, } as NewOutput; - return await this.create(soClient, newDefaultOutput); + return await this.create(soClient, newDefaultOutput, { + id: DEFAULT_OUTPUT_ID, + overwrite: true, + }); } return defaultOutput; @@ -118,7 +121,7 @@ class OutputService { public async create( soClient: SavedObjectsClientContract, output: NewOutput, - options?: { id?: string; fromPreconfiguration?: boolean } + options?: { id?: string; fromPreconfiguration?: boolean; overwrite?: boolean } ): Promise { const data: OutputSOAttributes = { ...output }; @@ -155,7 +158,7 @@ class OutputService { } const newSo = await soClient.create(SAVED_OBJECT_TYPE, data, { - overwrite: options?.fromPreconfiguration, + overwrite: options?.overwrite || options?.fromPreconfiguration, id: options?.id ? outputIdToUuid(options.id) : undefined, }); From 5c393dfad958c6c70f821e3b8a2bce328ef70e3b Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 2 Dec 2021 14:55:16 +0100 Subject: [PATCH 18/20] [Lens] fix not-working test for conflict for embeddables (#120211) --- .../plugins/lens/public/embeddable/embeddable.test.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 3b4edb8c72b076..ceb9388a561f00 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import React from 'react'; import { Embeddable, LensByValueInput, @@ -15,6 +15,7 @@ import { LensUnwrapResult, } from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; +import { spacesPluginMock } from '../../../spaces/public/mocks'; import { Filter } from '@kbn/es-query'; import { Query, TimeRange, IndexPatternsContract } from 'src/plugins/data/public'; import { Document } from '../persistence'; @@ -263,6 +264,10 @@ describe('embeddable', () => { } as LensUnwrapResult); } ); + const spacesPluginStart = spacesPluginMock.createStartContract(); + spacesPluginStart.ui.components.getEmbeddableLegacyUrlConflict = jest.fn(() => ( + <>getEmbeddableLegacyUrlConflict + )); const embeddable = new Embeddable( { timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, @@ -271,6 +276,7 @@ describe('embeddable', () => { expressionRenderer, basePath, indexPatternService: {} as IndexPatternsContract, + spaces: spacesPluginStart, capabilities: { canSaveDashboards: true, canSaveVisualizations: true, @@ -293,7 +299,9 @@ describe('embeddable', () => { {} as LensEmbeddableInput ); await embeddable.initializeSavedVis({} as LensEmbeddableInput); + embeddable.render(mountpoint); expect(expressionRenderer).toHaveBeenCalledTimes(0); + expect(spacesPluginStart.ui.components.getEmbeddableLegacyUrlConflict).toHaveBeenCalled(); }); it('should initialize output with deduped list of index patterns', async () => { From 54717c179bac36c6eda8450835ff99333d7f2117 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Thu, 2 Dec 2021 09:05:56 -0500 Subject: [PATCH 19/20] [Cases] Removing alerts internal client (#119901) * Removing alerts internal client * Fixing test errors --- .../cases/server/client/alerts/client.ts | 25 --- .../plugins/cases/server/client/alerts/get.ts | 11 +- .../server/client/alerts/update_status.ts | 21 -- .../cases/server/client/attachments/add.ts | 10 +- .../cases/server/client/cases/client.ts | 2 +- .../plugins/cases/server/client/cases/push.ts | 5 +- .../cases/server/client/cases/update.ts | 15 +- x-pack/plugins/cases/server/client/client.ts | 2 +- .../cases/server/client/client_internal.ts | 7 - x-pack/plugins/cases/server/client/factory.ts | 3 +- .../cases/server/client/sub_cases/client.ts | 10 +- .../cases/server/client/sub_cases/update.ts | 16 +- x-pack/plugins/cases/server/client/types.ts | 7 +- .../server/services/alerts/index.test.ts | 212 +++++++++--------- .../cases/server/services/alerts/index.ts | 206 ++++++++--------- x-pack/plugins/cases/server/services/index.ts | 1 - x-pack/plugins/cases/server/services/mocks.ts | 17 +- 17 files changed, 239 insertions(+), 331 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/alerts/client.ts delete mode 100644 x-pack/plugins/cases/server/client/alerts/update_status.ts diff --git a/x-pack/plugins/cases/server/client/alerts/client.ts b/x-pack/plugins/cases/server/client/alerts/client.ts deleted file mode 100644 index aaa8bdb2047aa4..00000000000000 --- a/x-pack/plugins/cases/server/client/alerts/client.ts +++ /dev/null @@ -1,25 +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 { AlertGet, AlertUpdateStatus, CasesClientGetAlertsResponse } from './types'; -import { get } from './get'; -import { updateStatus } from './update_status'; -import { CasesClientArgs } from '../types'; - -export interface AlertSubClient { - get(args: AlertGet): Promise; - updateStatus(args: AlertUpdateStatus): Promise; -} - -export const createAlertsSubClient = (clientArgs: CasesClientArgs): AlertSubClient => { - const alertsSubClient: AlertSubClient = { - get: (params: AlertGet) => get(params, clientArgs), - updateStatus: (params: AlertUpdateStatus) => updateStatus(params, clientArgs), - }; - - return Object.freeze(alertsSubClient); -}; diff --git a/x-pack/plugins/cases/server/client/alerts/get.ts b/x-pack/plugins/cases/server/client/alerts/get.ts index 2048ccae4fa60e..d95c4f536abc34 100644 --- a/x-pack/plugins/cases/server/client/alerts/get.ts +++ b/x-pack/plugins/cases/server/client/alerts/get.ts @@ -5,19 +5,20 @@ * 2.0. */ -import { CasesClientGetAlertsResponse, AlertGet } from './types'; +import { CasesClientGetAlertsResponse } from './types'; import { CasesClientArgs } from '..'; +import { AlertInfo } from '../../common'; -export const get = async ( - { alertsInfo }: AlertGet, +export const getAlerts = async ( + alertsInfo: AlertInfo[], clientArgs: CasesClientArgs ): Promise => { - const { alertsService, scopedClusterClient, logger } = clientArgs; + const { alertsService } = clientArgs; if (alertsInfo.length === 0) { return []; } - const alerts = await alertsService.getAlerts({ alertsInfo, scopedClusterClient, logger }); + const alerts = await alertsService.getAlerts(alertsInfo); if (!alerts) { return []; } diff --git a/x-pack/plugins/cases/server/client/alerts/update_status.ts b/x-pack/plugins/cases/server/client/alerts/update_status.ts deleted file mode 100644 index a0684b59241b07..00000000000000 --- a/x-pack/plugins/cases/server/client/alerts/update_status.ts +++ /dev/null @@ -1,21 +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 { CasesClientArgs } from '..'; -import { UpdateAlertRequest } from './types'; - -interface UpdateAlertsStatusArgs { - alerts: UpdateAlertRequest[]; -} - -export const updateStatus = async ( - { alerts }: UpdateAlertsStatusArgs, - clientArgs: CasesClientArgs -): Promise => { - const { alertsService, scopedClusterClient, logger } = clientArgs; - await alertsService.updateAlertsStatus({ alerts, scopedClusterClient, logger }); -}; diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index 7f6ede930e4c38..84dbc8921f0e44 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -127,6 +127,7 @@ const addGeneratedAlerts = async ( logger, lensEmbeddableFactory, authorization, + alertsService, } = clientArgs; const query = pipe( @@ -204,9 +205,7 @@ const addGeneratedAlerts = async ( comment: query, status: subCase.attributes.status, }); - await casesClientInternal.alerts.updateStatus({ - alerts: alertsToUpdate, - }); + await alertsService.updateAlertsStatus(alertsToUpdate); } await userActionService.bulkCreate({ @@ -339,6 +338,7 @@ export const addComment = async ( logger, lensEmbeddableFactory, authorization, + alertsService, } = clientArgs; if (isCommentRequestTypeGenAlert(comment)) { @@ -392,9 +392,7 @@ export const addComment = async ( status: updatedCase.status, }); - await casesClientInternal.alerts.updateStatus({ - alerts: alertsToUpdate, - }); + await alertsService.updateAlertsStatus(alertsToUpdate); } await userActionService.bulkCreate({ diff --git a/x-pack/plugins/cases/server/client/cases/client.ts b/x-pack/plugins/cases/server/client/cases/client.ts index fd9bd489f31b2d..09386431200edd 100644 --- a/x-pack/plugins/cases/server/client/cases/client.ts +++ b/x-pack/plugins/cases/server/client/cases/client.ts @@ -108,7 +108,7 @@ export const createCasesSubClient = ( get: (params: GetParams) => get(params, clientArgs), resolve: (params: GetParams) => resolve(params, clientArgs), push: (params: PushParams) => push(params, clientArgs, casesClient, casesClientInternal), - update: (cases: CasesPatchRequest) => update(cases, clientArgs, casesClientInternal), + update: (cases: CasesPatchRequest) => update(cases, clientArgs), delete: (ids: string[]) => deleteCases(ids, clientArgs), getTags: (params: AllTagsFindRequest) => getTags(params, clientArgs), getReporters: (params: AllReportersFindRequest) => getReporters(params, clientArgs), diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 1b090a653546d8..953f8b88c990b9 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -26,6 +26,7 @@ import { createCaseError, flattenCaseSavedObject, getAlertInfoFromComments } fro import { CasesClient, CasesClientArgs, CasesClientInternal } from '..'; import { Operations } from '../../authorization'; import { casesConnectors } from '../../connectors'; +import { getAlerts } from '../alerts/get'; /** * Returns true if the case should be closed based on the configuration settings and whether the case @@ -106,9 +107,7 @@ export const push = async ( const alertsInfo = getAlertInfoFromComments(theCase?.comments); - const alerts = await casesClientInternal.alerts.get({ - alertsInfo, - }); + const alerts = await getAlerts(alertsInfo, clientArgs); const getMappingsResponse = await casesClientInternal.configuration.getMappings({ connector: theCase.connector, diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index da1c25c83a37b1..455665dc7012ce 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -44,7 +44,7 @@ import { import { buildCaseUserActions } from '../../services/user_actions/helpers'; import { getCaseToUpdate } from '../utils'; -import { CasesService } from '../../services'; +import { AlertService, CasesService } from '../../services'; import { createAlertUpdateRequest, createCaseError, @@ -52,7 +52,6 @@ import { isCommentRequestTypeAlertOrGenAlert, } from '../../common'; import { UpdateAlertRequest } from '../alerts/types'; -import { CasesClientInternal } from '../client_internal'; import { CasesClientArgs } from '..'; import { Operations, OwnerEntity } from '../../authorization'; @@ -306,13 +305,13 @@ async function updateAlerts({ casesWithStatusChangedAndSynced, caseService, unsecuredSavedObjectsClient, - casesClientInternal, + alertsService, }: { casesWithSyncSettingChangedToOn: UpdateRequestWithOriginalCase[]; casesWithStatusChangedAndSynced: UpdateRequestWithOriginalCase[]; caseService: CasesService; unsecuredSavedObjectsClient: SavedObjectsClientContract; - casesClientInternal: CasesClientInternal; + alertsService: AlertService; }) { /** * It's possible that a case ID can appear multiple times in each array. I'm intentionally placing the status changes @@ -361,7 +360,7 @@ async function updateAlerts({ [] ); - await casesClientInternal.alerts.updateStatus({ alerts: alertsToUpdate }); + await alertsService.updateAlertsStatus(alertsToUpdate); } function partitionPatchRequest( @@ -410,8 +409,7 @@ interface UpdateRequestWithOriginalCase { */ export const update = async ( cases: CasesPatchRequest, - clientArgs: CasesClientArgs, - casesClientInternal: CasesClientInternal + clientArgs: CasesClientArgs ): Promise => { const { unsecuredSavedObjectsClient, @@ -420,6 +418,7 @@ export const update = async ( user, logger, authorization, + alertsService, } = clientArgs; const query = pipe( excess(CasesPatchRequestRt).decode(cases), @@ -568,7 +567,7 @@ export const update = async ( casesWithSyncSettingChangedToOn, caseService, unsecuredSavedObjectsClient, - casesClientInternal, + alertsService, }); const returnUpdatedCase = myCases.saved_objects diff --git a/x-pack/plugins/cases/server/client/client.ts b/x-pack/plugins/cases/server/client/client.ts index 1d5300c5880b2a..bd4e36bb7c1778 100644 --- a/x-pack/plugins/cases/server/client/client.ts +++ b/x-pack/plugins/cases/server/client/client.ts @@ -34,7 +34,7 @@ export class CasesClient { this._cases = createCasesSubClient(args, this, this._casesClientInternal); this._attachments = createAttachmentsSubClient(args, this, this._casesClientInternal); this._userActions = createUserActionsSubClient(args); - this._subCases = createSubCasesClient(args, this._casesClientInternal); + this._subCases = createSubCasesClient(args); this._configure = createConfigurationSubClient(args, this._casesClientInternal); this._stats = createStatsSubClient(args); this._metrics = createMetricsSubClient(args, this); diff --git a/x-pack/plugins/cases/server/client/client_internal.ts b/x-pack/plugins/cases/server/client/client_internal.ts index 3623498223da72..57b9a0ffbc243b 100644 --- a/x-pack/plugins/cases/server/client/client_internal.ts +++ b/x-pack/plugins/cases/server/client/client_internal.ts @@ -6,25 +6,18 @@ */ import { CasesClientArgs } from './types'; -import { AlertSubClient, createAlertsSubClient } from './alerts/client'; import { InternalConfigureSubClient, createInternalConfigurationSubClient, } from './configure/client'; export class CasesClientInternal { - private readonly _alerts: AlertSubClient; private readonly _configuration: InternalConfigureSubClient; constructor(args: CasesClientArgs) { - this._alerts = createAlertsSubClient(args); this._configuration = createInternalConfigurationSubClient(args, this); } - public get alerts() { - return this._alerts; - } - public get configuration() { return this._configuration; } diff --git a/x-pack/plugins/cases/server/client/factory.ts b/x-pack/plugins/cases/server/client/factory.ts index 2fae6996f4aa2a..4f506b5e0b4f72 100644 --- a/x-pack/plugins/cases/server/client/factory.ts +++ b/x-pack/plugins/cases/server/client/factory.ts @@ -95,8 +95,7 @@ export class CasesClientFactory { const userInfo = caseService.getUser({ request }); return createCasesClient({ - alertsService: new AlertService(), - scopedClusterClient, + alertsService: new AlertService(scopedClusterClient, this.logger), unsecuredSavedObjectsClient: savedObjectsService.getScopedClient(request, { includedHiddenTypes: SAVED_OBJECT_TYPES, // this tells the security plugin to not perform SO authorization and audit logging since we are handling diff --git a/x-pack/plugins/cases/server/client/sub_cases/client.ts b/x-pack/plugins/cases/server/client/sub_cases/client.ts index 52aa8431e2975e..9b0395bbcb3b60 100644 --- a/x-pack/plugins/cases/server/client/sub_cases/client.ts +++ b/x-pack/plugins/cases/server/client/sub_cases/client.ts @@ -21,7 +21,7 @@ import { SubCasesFindResponseRt, SubCasesPatchRequest, } from '../../../common'; -import { CasesClientArgs, CasesClientInternal } from '..'; +import { CasesClientArgs } from '..'; import { countAlertsForID, createCaseError, @@ -85,16 +85,12 @@ export interface SubCasesClient { * * @ignore */ -export function createSubCasesClient( - clientArgs: CasesClientArgs, - casesClientInternal: CasesClientInternal -): SubCasesClient { +export function createSubCasesClient(clientArgs: CasesClientArgs): SubCasesClient { return Object.freeze({ delete: (ids: string[]) => deleteSubCase(ids, clientArgs), find: (findArgs: FindArgs) => find(findArgs, clientArgs), get: (getArgs: GetArgs) => get(getArgs, clientArgs), - update: (subCases: SubCasesPatchRequest) => - update({ subCases, clientArgs, casesClientInternal }), + update: (subCases: SubCasesPatchRequest) => update({ subCases, clientArgs }), }); } diff --git a/x-pack/plugins/cases/server/client/sub_cases/update.ts b/x-pack/plugins/cases/server/client/sub_cases/update.ts index c0d3d571bb1e84..3f602f7979d1f2 100644 --- a/x-pack/plugins/cases/server/client/sub_cases/update.ts +++ b/x-pack/plugins/cases/server/client/sub_cases/update.ts @@ -17,7 +17,7 @@ import { } from 'kibana/server'; import { nodeBuilder } from '@kbn/es-query'; -import { CasesService } from '../../services'; +import { AlertService, CasesService } from '../../services'; import { CASE_COMMENT_SAVED_OBJECT, CaseStatuses, @@ -46,7 +46,6 @@ import { } from '../../common'; import { UpdateAlertRequest } from '../../client/alerts/types'; import { CasesClientArgs } from '../types'; -import { CasesClientInternal } from '../client_internal'; function checkNonExistingOrConflict( toUpdate: SubCasePatchRequest[], @@ -208,13 +207,13 @@ async function getAlertComments({ async function updateAlerts({ caseService, unsecuredSavedObjectsClient, - casesClientInternal, + alertsService, logger, subCasesToSync, }: { caseService: CasesService; unsecuredSavedObjectsClient: SavedObjectsClientContract; - casesClientInternal: CasesClientInternal; + alertsService: AlertService; logger: Logger; subCasesToSync: SubCasePatchRequest[]; }) { @@ -246,7 +245,7 @@ async function updateAlerts({ [] ); - await casesClientInternal.alerts.updateStatus({ alerts: alertsToUpdate }); + await alertsService.updateAlertsStatus(alertsToUpdate); } catch (error) { throw createCaseError({ message: `Failed to update alert status while updating sub cases: ${JSON.stringify( @@ -264,11 +263,9 @@ async function updateAlerts({ export async function update({ subCases, clientArgs, - casesClientInternal, }: { subCases: SubCasesPatchRequest; clientArgs: CasesClientArgs; - casesClientInternal: CasesClientInternal; }): Promise { const query = pipe( excess(SubCasesPatchRequestRt).decode(subCases), @@ -276,7 +273,8 @@ export async function update({ ); try { - const { unsecuredSavedObjectsClient, user, caseService, userActionService } = clientArgs; + const { unsecuredSavedObjectsClient, user, caseService, userActionService, alertsService } = + clientArgs; const bulkSubCases = await caseService.getSubCases({ unsecuredSavedObjectsClient, @@ -358,7 +356,7 @@ export async function update({ await updateAlerts({ caseService, unsecuredSavedObjectsClient, - casesClientInternal, + alertsService, subCasesToSync: subCasesToSyncAlertsFor, logger: clientArgs.logger, }); diff --git a/x-pack/plugins/cases/server/client/types.ts b/x-pack/plugins/cases/server/client/types.ts index 27829d2539c7d8..f6c97df4f8b71c 100644 --- a/x-pack/plugins/cases/server/client/types.ts +++ b/x-pack/plugins/cases/server/client/types.ts @@ -6,16 +6,16 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import { ElasticsearchClient, SavedObjectsClientContract, Logger } from 'kibana/server'; +import { SavedObjectsClientContract, Logger } from 'kibana/server'; import { User } from '../../common'; import { Authorization } from '../authorization/authorization'; import { - AlertServiceContract, CaseConfigureService, CasesService, CaseUserActionService, ConnectorMappingsService, AttachmentService, + AlertService, } from '../services'; import { ActionsClient } from '../../../actions/server'; import { LensServerPluginSetup } from '../../../lens/server'; @@ -24,14 +24,13 @@ import { LensServerPluginSetup } from '../../../lens/server'; * Parameters for initializing a cases client */ export interface CasesClientArgs { - readonly scopedClusterClient: ElasticsearchClient; readonly caseConfigureService: CaseConfigureService; readonly caseService: CasesService; readonly connectorMappingsService: ConnectorMappingsService; readonly user: User; readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; readonly userActionService: CaseUserActionService; - readonly alertsService: AlertServiceContract; + readonly alertsService: AlertService; readonly attachmentService: AttachmentService; readonly logger: Logger; readonly lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory']; diff --git a/x-pack/plugins/cases/server/services/alerts/index.test.ts b/x-pack/plugins/cases/server/services/alerts/index.test.ts index 9113b73de187ac..2c98da198fa073 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.test.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.test.ts @@ -6,96 +6,96 @@ */ import { CaseStatuses } from '../../../common'; -import { AlertService, AlertServiceContract } from '.'; +import { AlertService } from '.'; import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; -import { ALERT_WORKFLOW_STATUS } from '../../../../rule_registry/common/technical_rule_data_field_names'; describe('updateAlertsStatus', () => { const esClient = elasticsearchServiceMock.createElasticsearchClient(); const logger = loggingSystemMock.create().get('case'); describe('happy path', () => { - let alertService: AlertServiceContract; + let alertService: AlertService; beforeEach(async () => { - alertService = new AlertService(); + alertService = new AlertService(esClient, logger); jest.resetAllMocks(); }); it('updates the status of the alert correctly', async () => { - const args = { - alerts: [{ id: 'alert-id-1', index: '.siem-signals', status: CaseStatuses.closed }], - scopedClusterClient: esClient, - logger, - }; + const args = [{ id: 'alert-id-1', index: '.siem-signals', status: CaseStatuses.closed }]; await alertService.updateAlertsStatus(args); - expect(esClient.updateByQuery).toHaveBeenCalledWith({ - index: '.siem-signals', - conflicts: 'abort', - body: { - script: { - source: `if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'closed' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'closed' - }`, - lang: 'painless', - }, - query: { - ids: { - values: ['alert-id-1'], + expect(esClient.updateByQuery.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "body": Object { + "query": Object { + "ids": Object { + "values": Array [ + "alert-id-1", + ], + }, + }, + "script": Object { + "lang": "painless", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'closed' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'closed' + }", + }, }, + "conflicts": "abort", + "ignore_unavailable": true, + "index": ".siem-signals", }, - }, - ignore_unavailable: true, - }); + ] + `); }); it('buckets the alerts by index', async () => { - const args = { - alerts: [ - { id: 'id1', index: '1', status: CaseStatuses.closed }, - { id: 'id2', index: '1', status: CaseStatuses.closed }, - ], - scopedClusterClient: esClient, - logger, - }; + const args = [ + { id: 'id1', index: '1', status: CaseStatuses.closed }, + { id: 'id2', index: '1', status: CaseStatuses.closed }, + ]; await alertService.updateAlertsStatus(args); expect(esClient.updateByQuery).toBeCalledTimes(1); - expect(esClient.updateByQuery).toHaveBeenCalledWith({ - index: '1', - conflicts: 'abort', - body: { - script: { - source: `if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'closed' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'closed' - }`, - lang: 'painless', - }, - query: { - ids: { - values: ['id1', 'id2'], + expect(esClient.updateByQuery.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "body": Object { + "query": Object { + "ids": Object { + "values": Array [ + "id1", + "id2", + ], + }, + }, + "script": Object { + "lang": "painless", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'closed' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'closed' + }", + }, }, + "conflicts": "abort", + "ignore_unavailable": true, + "index": "1", }, - }, - ignore_unavailable: true, - }); + ] + `); }); it('translates in-progress to acknowledged', async () => { - const args = { - alerts: [{ id: 'id1', index: '1', status: CaseStatuses['in-progress'] }], - scopedClusterClient: esClient, - logger, - }; + const args = [{ id: 'id1', index: '1', status: CaseStatuses['in-progress'] }]; await alertService.updateAlertsStatus(args); @@ -113,12 +113,12 @@ describe('updateAlertsStatus', () => { }, "script": Object { "lang": "painless", - "source": "if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'acknowledged' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'acknowledged' - }", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'acknowledged' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'acknowledged' + }", }, }, "conflicts": "abort", @@ -130,14 +130,10 @@ describe('updateAlertsStatus', () => { }); it('makes two calls when the statuses are different', async () => { - const args = { - alerts: [ - { id: 'id1', index: '1', status: CaseStatuses.closed }, - { id: 'id2', index: '1', status: CaseStatuses.open }, - ], - scopedClusterClient: esClient, - logger, - }; + const args = [ + { id: 'id1', index: '1', status: CaseStatuses.closed }, + { id: 'id2', index: '1', status: CaseStatuses.open }, + ]; await alertService.updateAlertsStatus(args); @@ -156,12 +152,12 @@ describe('updateAlertsStatus', () => { }, "script": Object { "lang": "painless", - "source": "if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'closed' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'closed' - }", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'closed' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'closed' + }", }, }, "conflicts": "abort", @@ -185,12 +181,12 @@ describe('updateAlertsStatus', () => { }, "script": Object { "lang": "painless", - "source": "if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'open' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'open' - }", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'open' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'open' + }", }, }, "conflicts": "abort", @@ -202,14 +198,10 @@ describe('updateAlertsStatus', () => { }); it('makes two calls when the indices are different', async () => { - const args = { - alerts: [ - { id: 'id1', index: '1', status: CaseStatuses.closed }, - { id: 'id2', index: '2', status: CaseStatuses.open }, - ], - scopedClusterClient: esClient, - logger, - }; + const args = [ + { id: 'id1', index: '1', status: CaseStatuses.closed }, + { id: 'id2', index: '2', status: CaseStatuses.open }, + ]; await alertService.updateAlertsStatus(args); @@ -228,12 +220,12 @@ describe('updateAlertsStatus', () => { }, "script": Object { "lang": "painless", - "source": "if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'closed' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'closed' - }", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'closed' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'closed' + }", }, }, "conflicts": "abort", @@ -257,12 +249,12 @@ describe('updateAlertsStatus', () => { }, "script": Object { "lang": "painless", - "source": "if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = 'open' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = 'open' - }", + "source": "if (ctx._source['kibana.alert.workflow_status'] != null) { + ctx._source['kibana.alert.workflow_status'] = 'open' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = 'open' + }", }, }, "conflicts": "abort", @@ -274,11 +266,9 @@ describe('updateAlertsStatus', () => { }); it('ignores empty indices', async () => { - await alertService.updateAlertsStatus({ - alerts: [{ id: 'alert-id-1', index: '', status: CaseStatuses.open }], - scopedClusterClient: esClient, - logger, - }); + await alertService.updateAlertsStatus([ + { id: 'alert-id-1', index: '', status: CaseStatuses.open }, + ]); expect(esClient.updateByQuery).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/cases/server/services/alerts/index.ts b/x-pack/plugins/cases/server/services/alerts/index.ts index d8a09fe1baf231..ca7bfe66804f33 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.ts @@ -8,8 +8,6 @@ import pMap from 'p-map'; import { isEmpty } from 'lodash'; -import type { PublicMethodsOf } from '@kbn/utility-types'; - import { ElasticsearchClient, Logger } from 'kibana/server'; import { CaseStatuses, MAX_ALERTS_PER_SUB_CASE, MAX_CONCURRENT_SEARCHES } from '../../../common'; import { AlertInfo, createCaseError } from '../../common'; @@ -19,20 +17,6 @@ import { STATUS_VALUES, } from '../../../../rule_registry/common/technical_rule_data_field_names'; -export type AlertServiceContract = PublicMethodsOf; - -interface UpdateAlertsStatusArgs { - alerts: UpdateAlertRequest[]; - scopedClusterClient: ElasticsearchClient; - logger: Logger; -} - -interface GetAlertsArgs { - alertsInfo: AlertInfo[]; - scopedClusterClient: ElasticsearchClient; - logger: Logger; -} - interface Alert { _id: string; _index: string; @@ -48,31 +32,112 @@ function isEmptyAlert(alert: AlertInfo): boolean { } export class AlertService { - public async updateAlertsStatus({ alerts, scopedClusterClient, logger }: UpdateAlertsStatusArgs) { + constructor( + private readonly scopedClusterClient: ElasticsearchClient, + private readonly logger: Logger + ) {} + + public async updateAlertsStatus(alerts: UpdateAlertRequest[]) { try { - const bucketedAlerts = bucketAlertsByIndexAndStatus(alerts, logger); + const bucketedAlerts = this.bucketAlertsByIndexAndStatus(alerts); const indexBuckets = Array.from(bucketedAlerts.entries()); await pMap( indexBuckets, async (indexBucket: [string, Map]) => - updateByQuery(indexBucket, scopedClusterClient), + this.updateByQuery(indexBucket), { concurrency: MAX_CONCURRENT_SEARCHES } ); } catch (error) { throw createCaseError({ message: `Failed to update alert status ids: ${JSON.stringify(alerts)}: ${error}`, error, - logger, + logger: this.logger, }); } } - public async getAlerts({ - scopedClusterClient, - alertsInfo, - logger, - }: GetAlertsArgs): Promise { + private bucketAlertsByIndexAndStatus( + alerts: UpdateAlertRequest[] + ): Map> { + return alerts.reduce>>( + (acc, alert) => { + // skip any alerts that are empty + if (isEmptyAlert(alert)) { + return acc; + } + + const translatedAlert = { ...alert, status: this.translateStatus(alert) }; + const statusToAlertId = acc.get(translatedAlert.index); + + // if we haven't seen the index before + if (!statusToAlertId) { + // add a new index in the parent map, with an entry for the status the alert set to pointing + // to an initial array of only the current alert + acc.set(translatedAlert.index, createStatusToAlertMap(translatedAlert)); + } else { + // We had the index in the map so check to see if we have a bucket for the + // status, if not add a new status entry with the alert, if so update the status entry + // with the alert + updateIndexEntryWithStatus(statusToAlertId, translatedAlert); + } + + return acc; + }, + new Map() + ); + } + + private translateStatus(alert: UpdateAlertRequest): STATUS_VALUES { + const translatedStatuses: Record = { + [CaseStatuses.open]: 'open', + [CaseStatuses['in-progress']]: 'acknowledged', + [CaseStatuses.closed]: 'closed', + }; + + const translatedStatus = translatedStatuses[alert.status]; + if (!translatedStatus) { + this.logger.error( + `Unable to translate case status ${alert.status} during alert update: ${JSON.stringify( + alert + )}` + ); + } + return translatedStatus ?? 'open'; + } + + private async updateByQuery([index, statusToAlertMap]: [ + string, + Map + ]) { + const statusBuckets = Array.from(statusToAlertMap); + return Promise.all( + // this will create three update by query calls one for each of the three statuses + statusBuckets.map(([status, translatedAlerts]) => + this.scopedClusterClient.updateByQuery({ + index, + conflicts: 'abort', + body: { + script: { + source: `if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { + ctx._source['${ALERT_WORKFLOW_STATUS}'] = '${status}' + } + if (ctx._source.signal != null && ctx._source.signal.status != null) { + ctx._source.signal.status = '${status}' + }`, + lang: 'painless', + }, + // the query here will contain all the ids that have the same status for the same index + // being updated + query: { ids: { values: translatedAlerts.map(({ id }) => id) } }, + }, + ignore_unavailable: true, + }) + ) + ); + } + + public async getAlerts(alertsInfo: AlertInfo[]): Promise { try { const docs = alertsInfo .filter((alert) => !isEmptyAlert(alert)) @@ -83,7 +148,7 @@ export class AlertService { return; } - const results = await scopedClusterClient.mget({ body: { docs } }); + const results = await this.scopedClusterClient.mget({ body: { docs } }); // @ts-expect-error @elastic/elasticsearch _source is optional return results.body; @@ -91,7 +156,7 @@ export class AlertService { throw createCaseError({ message: `Failed to retrieve alerts ids: ${JSON.stringify(alertsInfo)}: ${error}`, error, - logger, + logger: this.logger, }); } } @@ -103,62 +168,6 @@ interface TranslatedUpdateAlertRequest { status: STATUS_VALUES; } -function bucketAlertsByIndexAndStatus( - alerts: UpdateAlertRequest[], - logger: Logger -): Map> { - return alerts.reduce>>( - (acc, alert) => { - // skip any alerts that are empty - if (isEmptyAlert(alert)) { - return acc; - } - - const translatedAlert = { ...alert, status: translateStatus({ alert, logger }) }; - const statusToAlertId = acc.get(translatedAlert.index); - - // if we haven't seen the index before - if (!statusToAlertId) { - // add a new index in the parent map, with an entry for the status the alert set to pointing - // to an initial array of only the current alert - acc.set(translatedAlert.index, createStatusToAlertMap(translatedAlert)); - } else { - // We had the index in the map so check to see if we have a bucket for the - // status, if not add a new status entry with the alert, if so update the status entry - // with the alert - updateIndexEntryWithStatus(statusToAlertId, translatedAlert); - } - - return acc; - }, - new Map() - ); -} - -function translateStatus({ - alert, - logger, -}: { - alert: UpdateAlertRequest; - logger: Logger; -}): STATUS_VALUES { - const translatedStatuses: Record = { - [CaseStatuses.open]: 'open', - [CaseStatuses['in-progress']]: 'acknowledged', - [CaseStatuses.closed]: 'closed', - }; - - const translatedStatus = translatedStatuses[alert.status]; - if (!translatedStatus) { - logger.error( - `Unable to translate case status ${alert.status} during alert update: ${JSON.stringify( - alert - )}` - ); - } - return translatedStatus ?? 'open'; -} - function createStatusToAlertMap( alert: TranslatedUpdateAlertRequest ): Map { @@ -177,34 +186,3 @@ function updateIndexEntryWithStatus( statusBucket.push(alert); } } - -async function updateByQuery( - [index, statusToAlertMap]: [string, Map], - scopedClusterClient: ElasticsearchClient -) { - const statusBuckets = Array.from(statusToAlertMap); - return Promise.all( - // this will create three update by query calls one for each of the three statuses - statusBuckets.map(([status, translatedAlerts]) => - scopedClusterClient.updateByQuery({ - index, - conflicts: 'abort', - body: { - script: { - source: `if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null) { - ctx._source['${ALERT_WORKFLOW_STATUS}'] = '${status}' - } - if (ctx._source.signal != null && ctx._source.signal.status != null) { - ctx._source.signal.status = '${status}' - }`, - lang: 'painless', - }, - // the query here will contain all the ids that have the same status for the same index - // being updated - query: { ids: { values: translatedAlerts.map(({ id }) => id) } }, - }, - ignore_unavailable: true, - }) - ) - ); -} diff --git a/x-pack/plugins/cases/server/services/index.ts b/x-pack/plugins/cases/server/services/index.ts index a1cb5d8138c400..c14e1fab4a4105 100644 --- a/x-pack/plugins/cases/server/services/index.ts +++ b/x-pack/plugins/cases/server/services/index.ts @@ -12,7 +12,6 @@ export { CasesService } from './cases'; export { CaseConfigureService } from './configure'; export { CaseUserActionService } from './user_actions'; export { ConnectorMappingsService } from './connector_mappings'; -export type { AlertServiceContract } from './alerts'; export { AlertService } from './alerts'; export { AttachmentService } from './attachments'; diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index 1ea9f481d302ff..f46bcd0906c600 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -7,7 +7,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { - AlertServiceContract, + AlertService, CaseConfigureService, CasesService, CaseUserActionService, @@ -19,7 +19,7 @@ export type CaseServiceMock = jest.Mocked; export type CaseConfigureServiceMock = jest.Mocked; export type ConnectorMappingsServiceMock = jest.Mocked; export type CaseUserActionServiceMock = jest.Mocked; -export type AlertServiceMock = jest.Mocked; +export type AlertServiceMock = jest.Mocked; export type AttachmentServiceMock = jest.Mocked; export const createCaseServiceMock = (): CaseServiceMock => { @@ -93,10 +93,15 @@ export const createUserActionServiceMock = (): CaseUserActionServiceMock => { return service as unknown as CaseUserActionServiceMock; }; -export const createAlertServiceMock = (): AlertServiceMock => ({ - updateAlertsStatus: jest.fn(), - getAlerts: jest.fn(), -}); +export const createAlertServiceMock = (): AlertServiceMock => { + const service: PublicMethodsOf = { + updateAlertsStatus: jest.fn(), + getAlerts: jest.fn(), + }; + + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return service as unknown as AlertServiceMock; +}; export const createAttachmentServiceMock = (): AttachmentServiceMock => { const service: PublicMethodsOf = { From 90439793c27ba6460ce02f4f080584d0aea16d5d Mon Sep 17 00:00:00 2001 From: Abdul Wahab Zahid Date: Thu, 2 Dec 2021 15:33:51 +0100 Subject: [PATCH 20/20] Creating io-ts types for synthetics monitors. (#120105) Co-authored-by: Dominique Clarke Co-authored-by: shahzad31 --- .../monitor_management/config_key.ts | 71 +++++ .../runtime_types/monitor_management/index.ts | 11 + .../monitor_management/monitor_configs.ts | 121 ++++++++ .../monitor_management/monitor_meta_data.ts | 21 ++ .../monitor_management/monitor_types.ts | 195 ++++++++++++ x-pack/plugins/uptime/common/utils/t_enum.ts | 38 +++ .../browser/advanced_fields.test.tsx | 16 +- .../fleet_package/browser/advanced_fields.tsx | 28 +- .../fleet_package/browser/formatters.ts | 75 +++-- .../fleet_package/browser/normalizers.test.ts | 34 +-- .../fleet_package/browser/normalizers.ts | 72 ++--- .../fleet_package/browser/simple_fields.tsx | 49 +-- .../browser/throttling_fields.test.tsx | 6 +- .../browser/throttling_fields.tsx | 34 +-- .../browser/zip_url_tls_fields.test.tsx | 30 +- .../browser/zip_url_tls_fields.tsx | 49 ++- .../fleet_package/common/common_fields.tsx | 23 +- .../fleet_package/common/default_values.ts | 14 +- .../fleet_package/common/formatters.ts | 20 +- .../fleet_package/common/normalizers.ts | 28 +- .../contexts/browser_context.tsx | 54 ++-- .../contexts/browser_context_advanced.tsx | 44 +-- .../contexts/browser_provider.tsx | 14 +- .../fleet_package/contexts/http_context.tsx | 32 +- .../contexts/http_context_advanced.tsx | 54 ++-- .../fleet_package/contexts/http_provider.tsx | 12 +- .../fleet_package/contexts/icmp_context.tsx | 30 +- .../contexts/synthetics_context_providers.tsx | 4 +- .../fleet_package/contexts/tcp_context.tsx | 30 +- .../contexts/tcp_context_advanced.tsx | 32 +- .../fleet_package/contexts/tcp_provider.tsx | 14 +- .../contexts/tls_fields_context.tsx | 24 +- .../fleet_package/custom_fields.test.tsx | 30 +- .../fleet_package/custom_fields.tsx | 6 +- .../fleet_package/hooks/use_policy.ts | 22 +- .../hooks/use_update_policy.test.tsx | 218 +++++++------- .../fleet_package/hooks/use_update_policy.ts | 12 +- .../http/advanced_fields.test.tsx | 20 +- .../fleet_package/http/advanced_fields.tsx | 64 ++-- .../fleet_package/http/formatters.ts | 46 +-- .../fleet_package/http/normalizers.ts | 60 ++-- .../fleet_package/http/simple_fields.tsx | 24 +- .../fleet_package/icmp/formatters.ts | 6 +- .../fleet_package/icmp/normalizers.ts | 10 +- .../fleet_package/icmp/simple_fields.tsx | 24 +- .../fleet_package/schedule_field.tsx | 4 +- .../synthetics_policy_create_extension.tsx | 3 +- ...s_policy_create_extension_wrapper.test.tsx | 38 +-- .../synthetics_policy_edit_extension.tsx | 4 +- ...ics_policy_edit_extension_wrapper.test.tsx | 80 +++-- ...nthetics_policy_edit_extension_wrapper.tsx | 43 +-- .../tcp/advanced_fields.test.tsx | 10 +- .../fleet_package/tcp/advanced_fields.tsx | 22 +- .../fleet_package/tcp/formatters.ts | 14 +- .../fleet_package/tcp/normalizers.ts | 18 +- .../fleet_package/tcp/simple_fields.tsx | 18 +- .../fleet_package/tls/default_values.ts | 16 +- .../fleet_package/tls/formatters.ts | 24 +- .../fleet_package/tls/normalizers.ts | 40 +-- .../fleet_package/tls_fields.test.tsx | 30 +- .../components/fleet_package/tls_fields.tsx | 38 +-- .../public/components/fleet_package/types.tsx | 278 ++---------------- .../components/fleet_package/validation.tsx | 65 ++-- .../monitor_management/formatters/browser.ts | 52 ++-- .../monitor_management/formatters/common.ts | 20 +- .../monitor_management/formatters/http.ts | 42 +-- .../monitor_management/formatters/icmp.ts | 6 +- .../monitor_management/formatters/tcp.ts | 14 +- .../monitor_management/formatters/tls.ts | 16 +- .../hooks/use_format_monitor.ts | 22 +- .../uptime/public/pages/edit_monitor.tsx | 44 +-- 71 files changed, 1495 insertions(+), 1287 deletions(-) create mode 100644 x-pack/plugins/uptime/common/runtime_types/monitor_management/config_key.ts create mode 100644 x-pack/plugins/uptime/common/runtime_types/monitor_management/index.ts create mode 100644 x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_configs.ts create mode 100644 x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_meta_data.ts create mode 100644 x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts create mode 100644 x-pack/plugins/uptime/common/utils/t_enum.ts diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/config_key.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/config_key.ts new file mode 100644 index 00000000000000..13a7c1e1f2f2da --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/config_key.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { tEnum } from '../../utils/t_enum'; + +// values must match keys in the integration package +export enum ConfigKey { + APM_SERVICE_NAME = 'service.name', + HOSTS = 'hosts', + IGNORE_HTTPS_ERRORS = 'ignore_https_errors', + JOURNEY_FILTERS_MATCH = 'filter_journeys.match', + JOURNEY_FILTERS_TAGS = 'filter_journeys.tags', + MAX_REDIRECTS = 'max_redirects', + METADATA = '__ui', + MONITOR_TYPE = 'type', + NAME = 'name', + PARAMS = 'params', + PASSWORD = 'password', + PROXY_URL = 'proxy_url', + PROXY_USE_LOCAL_RESOLVER = 'proxy_use_local_resolver', + RESPONSE_BODY_CHECK_NEGATIVE = 'check.response.body.negative', + RESPONSE_BODY_CHECK_POSITIVE = 'check.response.body.positive', + RESPONSE_BODY_INDEX = 'response.include_body', + RESPONSE_HEADERS_CHECK = 'check.response.headers', + RESPONSE_HEADERS_INDEX = 'response.include_headers', + RESPONSE_RECEIVE_CHECK = 'check.receive', + RESPONSE_STATUS_CHECK = 'check.response.status', + REQUEST_BODY_CHECK = 'check.request.body', + REQUEST_HEADERS_CHECK = 'check.request.headers', + REQUEST_METHOD_CHECK = 'check.request.method', + REQUEST_SEND_CHECK = 'check.send', + SCHEDULE = 'schedule', + SCREENSHOTS = 'screenshots', + SOURCE_INLINE = 'source.inline.script', + SOURCE_ZIP_URL = 'source.zip_url.url', + SOURCE_ZIP_USERNAME = 'source.zip_url.username', + SOURCE_ZIP_PASSWORD = 'source.zip_url.password', + SOURCE_ZIP_FOLDER = 'source.zip_url.folder', + SOURCE_ZIP_PROXY_URL = 'source.zip_url.proxy_url', + SYNTHETICS_ARGS = 'synthetics_args', + TLS_CERTIFICATE_AUTHORITIES = 'ssl.certificate_authorities', + TLS_CERTIFICATE = 'ssl.certificate', + TLS_KEY = 'ssl.key', + TLS_KEY_PASSPHRASE = 'ssl.key_passphrase', + TLS_VERIFICATION_MODE = 'ssl.verification_mode', + TLS_VERSION = 'ssl.supported_protocols', + TAGS = 'tags', + TIMEOUT = 'timeout', + THROTTLING_CONFIG = 'throttling.config', + IS_THROTTLING_ENABLED = 'throttling.is_enabled', + DOWNLOAD_SPEED = 'throttling.download_speed', + UPLOAD_SPEED = 'throttling.upload_speed', + LATENCY = 'throttling.latency', + URLS = 'urls', + USERNAME = 'username', + WAIT = 'wait', + ZIP_URL_TLS_CERTIFICATE_AUTHORITIES = 'source.zip_url.ssl.certificate_authorities', + ZIP_URL_TLS_CERTIFICATE = 'source.zip_url.ssl.certificate', + ZIP_URL_TLS_KEY = 'source.zip_url.ssl.key', + ZIP_URL_TLS_KEY_PASSPHRASE = 'source.zip_url.ssl.key_passphrase', + ZIP_URL_TLS_VERIFICATION_MODE = 'source.zip_url.ssl.verification_mode', + ZIP_URL_TLS_VERSION = 'source.zip_url.ssl.supported_protocols', +} + +export const ConfigKeyCodec = tEnum('ConfigKey', ConfigKey); +export type ConfigKeyType = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/index.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/index.ts new file mode 100644 index 00000000000000..9e9ea0ab1f246c --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/index.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export * from './config_key'; +export * from './monitor_configs'; +export * from './monitor_meta_data'; +export * from './monitor_types'; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_configs.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_configs.ts new file mode 100644 index 00000000000000..ac917dda0e1420 --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_configs.ts @@ -0,0 +1,121 @@ +/* + * 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 * as t from 'io-ts'; +import { tEnum } from '../../utils/t_enum'; + +export enum DataStream { + HTTP = 'http', + TCP = 'tcp', + ICMP = 'icmp', + BROWSER = 'browser', +} + +export const DataStreamCodec = tEnum('DataStream', DataStream); +export type DataStreamType = t.TypeOf; + +export enum HTTPMethod { + GET = 'GET', + POST = 'POST', + PUT = 'PUT', + DELETE = 'DELETE', + HEAD = 'HEAD', +} + +export const HTTPMethodCodec = tEnum('HTTPMethod', HTTPMethod); +export type HTTPMethodType = t.TypeOf; + +export enum ResponseBodyIndexPolicy { + ALWAYS = 'always', + NEVER = 'never', + ON_ERROR = 'on_error', +} + +export const ResponseBodyIndexPolicyCodec = tEnum( + 'ResponseBodyIndexPolicy', + ResponseBodyIndexPolicy +); +export type ResponseBodyIndexPolicyType = t.TypeOf; + +export enum MonacoEditorLangId { + JSON = 'xjson', + PLAINTEXT = 'plaintext', + XML = 'xml', + JAVASCRIPT = 'javascript', +} + +export const MonacoEditorLangIdCodec = tEnum( + 'MonacoEditorLangId', + MonacoEditorLangId +); +export type MonacoEditorLangIdType = t.TypeOf; + +export enum Mode { + FORM = 'form', + JSON = 'json', + PLAINTEXT = 'text', + XML = 'xml', +} + +export const ModeCodec = tEnum('Mode', Mode); +export type ModeType = t.TypeOf; + +export enum ContentType { + JSON = 'application/json', + TEXT = 'text/plain', + XML = 'application/xml', + FORM = 'application/x-www-form-urlencoded', +} + +export const ContentTypeCodec = tEnum('ContentType', ContentType); +export type ContentTypeType = t.TypeOf; + +export enum ScheduleUnit { + MINUTES = 'm', + SECONDS = 's', +} + +export const ScheduleUnitCodec = tEnum('ScheduleUnit', ScheduleUnit); +export type ScheduleUnitType = t.TypeOf; + +export enum VerificationMode { + CERTIFICATE = 'certificate', + FULL = 'full', + NONE = 'none', + STRICT = 'strict', +} + +export const VerificationModeCodec = tEnum('VerificationMode', VerificationMode); +export type VerificationModeType = t.TypeOf; + +export enum TLSVersion { + ONE_ZERO = 'TLSv1.0', + ONE_ONE = 'TLSv1.1', + ONE_TWO = 'TLSv1.2', + ONE_THREE = 'TLSv1.3', +} + +export const TLSVersionCodec = tEnum('TLSVersion', TLSVersion); +export type TLSVersionType = t.TypeOf; + +export enum ScreenshotOption { + ON = 'on', + OFF = 'off', + ONLY_ON_FAILURE = 'only-on-failure', +} + +export const ScreenshotOptionCodec = tEnum('ScreenshotOption', ScreenshotOption); +export type ScreenshotOptionType = t.TypeOf; + +export enum ThrottlingSuffix { + DOWNLOAD = 'd', + UPLOAD = 'u', + LATENCY = 'l', +} + +export const ThrottlingSuffixCodec = tEnum('ThrottlingSuffix', ThrottlingSuffix); +export type ThrottlingSuffixType = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_meta_data.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_meta_data.ts new file mode 100644 index 00000000000000..da3ce0fab60212 --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_meta_data.ts @@ -0,0 +1,21 @@ +/* + * 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 * as t from 'io-ts'; + +const ScriptSourceCodec = t.interface({ + is_generated_script: t.boolean, + file_name: t.string, +}); + +export const MetadataCodec = t.partial({ + is_tls_enabled: t.boolean, + is_zip_url_tls_enabled: t.boolean, + script_source: ScriptSourceCodec, +}); + +export type Metadata = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts new file mode 100644 index 00000000000000..f54035b7f69ffb --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts @@ -0,0 +1,195 @@ +/* + * 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 * as t from 'io-ts'; +import { ConfigKey } from './config_key'; +import { + DataStreamCodec, + ModeCodec, + ResponseBodyIndexPolicyCodec, + ScheduleUnitCodec, +} from './monitor_configs'; +import { MetadataCodec } from './monitor_meta_data'; +import { TLSVersionCodec, VerificationModeCodec } from './monitor_configs'; + +const Schedule = t.interface({ + number: t.string, + unit: ScheduleUnitCodec, +}); + +// TLSFields +export const TLSFieldsCodec = t.partial({ + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: t.string, + [ConfigKey.TLS_CERTIFICATE]: t.string, + [ConfigKey.TLS_KEY]: t.string, + [ConfigKey.TLS_KEY_PASSPHRASE]: t.string, + [ConfigKey.TLS_VERIFICATION_MODE]: VerificationModeCodec, + [ConfigKey.TLS_VERSION]: t.array(TLSVersionCodec), +}); + +export type TLSFields = t.TypeOf; + +// ZipUrlTLSFields +export const ZipUrlTLSFieldsCodec = t.partial({ + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: t.string, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: t.string, + [ConfigKey.ZIP_URL_TLS_KEY]: t.string, + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: t.string, + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: VerificationModeCodec, + [ConfigKey.ZIP_URL_TLS_VERSION]: t.array(TLSVersionCodec), +}); + +export type ZipUrlTLSFields = t.TypeOf; + +// CommonFields +export const CommonFieldsCodec = t.interface({ + [ConfigKey.MONITOR_TYPE]: DataStreamCodec, + [ConfigKey.SCHEDULE]: Schedule, + [ConfigKey.APM_SERVICE_NAME]: t.string, + [ConfigKey.TIMEOUT]: t.string, + [ConfigKey.TAGS]: t.array(t.string), +}); + +export type CommonFields = t.TypeOf; + +// TCP Simple Fields +export const TCPSimpleFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.METADATA]: MetadataCodec, + [ConfigKey.HOSTS]: t.string, + }), + CommonFieldsCodec, +]); + +export type TCPSimpleFields = t.TypeOf; + +// TCPAdvancedFields +export const TCPAdvancedFieldsCodec = t.interface({ + [ConfigKey.PROXY_URL]: t.string, + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: t.boolean, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: t.string, + [ConfigKey.REQUEST_SEND_CHECK]: t.string, +}); + +export type TCPAdvancedFields = t.TypeOf; + +// TCPFields +export const TCPFieldsCodec = t.intersection([ + TCPSimpleFieldsCodec, + TCPAdvancedFieldsCodec, + TLSFieldsCodec, +]); + +export type TCPFields = t.TypeOf; + +// ICMP SimpleFields +export const ICMPSimpleFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.HOSTS]: t.string, + [ConfigKey.WAIT]: t.string, + }), + CommonFieldsCodec, +]); + +export type ICMPSimpleFields = t.TypeOf; +export type ICMPFields = t.TypeOf; + +// HTTPSimpleFields +export const HTTPSimpleFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.METADATA]: MetadataCodec, + [ConfigKey.MAX_REDIRECTS]: t.string, + [ConfigKey.URLS]: t.string, + }), + CommonFieldsCodec, +]); + +export type HTTPSimpleFields = t.TypeOf; + +// HTTPAdvancedFields +export const HTTPAdvancedFieldsCodec = t.interface({ + [ConfigKey.PASSWORD]: t.string, + [ConfigKey.PROXY_URL]: t.string, + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: t.array(t.string), + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: t.array(t.string), + [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicyCodec, + [ConfigKey.RESPONSE_HEADERS_CHECK]: t.record(t.string, t.string), + [ConfigKey.RESPONSE_HEADERS_INDEX]: t.boolean, + [ConfigKey.RESPONSE_STATUS_CHECK]: t.array(t.string), + [ConfigKey.REQUEST_BODY_CHECK]: t.interface({ value: t.string, type: ModeCodec }), + [ConfigKey.REQUEST_HEADERS_CHECK]: t.record(t.string, t.string), + [ConfigKey.REQUEST_METHOD_CHECK]: t.string, + [ConfigKey.USERNAME]: t.string, +}); + +export type HTTPAdvancedFields = t.TypeOf; + +// HTTPFields +export const HTTPFieldsCodec = t.intersection([ + HTTPSimpleFieldsCodec, + HTTPAdvancedFieldsCodec, + TLSFieldsCodec, +]); +export type HTTPFields = t.TypeOf; + +// Browser Fields +export const ThrottlingConfigKeyCodec = t.union([ + t.literal(ConfigKey.DOWNLOAD_SPEED), + t.literal(ConfigKey.UPLOAD_SPEED), + t.literal(ConfigKey.LATENCY), +]); + +export type ThrottlingConfigKey = t.TypeOf; + +export const BrowserSimpleFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.METADATA]: MetadataCodec, + [ConfigKey.SOURCE_INLINE]: t.string, + [ConfigKey.SOURCE_ZIP_URL]: t.string, + [ConfigKey.SOURCE_ZIP_FOLDER]: t.string, + [ConfigKey.SOURCE_ZIP_USERNAME]: t.string, + [ConfigKey.SOURCE_ZIP_PASSWORD]: t.string, + [ConfigKey.SOURCE_ZIP_PROXY_URL]: t.string, + [ConfigKey.PARAMS]: t.string, + }), + ZipUrlTLSFieldsCodec, + CommonFieldsCodec, +]); + +export const BrowserAdvancedFieldsCodec = t.interface({ + [ConfigKey.SYNTHETICS_ARGS]: t.array(t.string), + [ConfigKey.SCREENSHOTS]: t.string, + [ConfigKey.JOURNEY_FILTERS_MATCH]: t.string, + [ConfigKey.JOURNEY_FILTERS_TAGS]: t.array(t.string), + [ConfigKey.IGNORE_HTTPS_ERRORS]: t.boolean, + [ConfigKey.IS_THROTTLING_ENABLED]: t.boolean, + [ConfigKey.DOWNLOAD_SPEED]: t.string, + [ConfigKey.UPLOAD_SPEED]: t.string, + [ConfigKey.LATENCY]: t.string, + [ConfigKey.THROTTLING_CONFIG]: t.string, +}); + +export const BrowserFieldsCodec = t.intersection([ + BrowserSimpleFieldsCodec, + BrowserAdvancedFieldsCodec, +]); + +export type BrowserFields = t.TypeOf; +export type BrowserSimpleFields = t.TypeOf; +export type BrowserAdvancedFields = t.TypeOf; + +export const MonitorFieldsCodec = t.intersection([ + HTTPFieldsCodec, + TCPFieldsCodec, + ICMPSimpleFieldsCodec, + BrowserFieldsCodec, + t.interface({ + [ConfigKey.NAME]: t.string, + }), +]); + +export type MonitorFields = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/utils/t_enum.ts b/x-pack/plugins/uptime/common/utils/t_enum.ts new file mode 100644 index 00000000000000..e00a51dbdf4384 --- /dev/null +++ b/x-pack/plugins/uptime/common/utils/t_enum.ts @@ -0,0 +1,38 @@ +/* + * 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 * as t from 'io-ts'; + +/** + * This utility function can be used to turn a TypeScript enum into a io-ts codec. + * + * @example + * import { PathReporter } from "io-ts/lib/PathReporter"; + * + * enum Thing { + * FOO = "foo", + * BAR = "bar" + * } + * + * const ThingCodec = tEnum("Thing", Thing); + * + * console.log(PathReporter.report(ThingCodec.decode('invalidvalue'))); + * // prints [ 'Invalid value "invalidvalue" supplied to : Thing' ] + * console.log(PathReporter.report(ThingCodec.decode('foo'))); + * // prints [ 'No errors!' ] + */ +export function tEnum(enumName: string, theEnum: Record) { + const isEnumValue = (input: unknown): input is EnumType => + Object.values(theEnum).includes(input); + + return new t.Type( + enumName, + isEnumValue, + (input, context) => (isEnumValue(input) ? t.success(input) : t.failure(input, context)), + t.identity + ); +} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx index f0900ae2595926..2675b6e4c617e2 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx @@ -10,11 +10,11 @@ import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; import { BrowserAdvancedFields } from './advanced_fields'; import { - ConfigKeys, - DataStream, - IBrowserAdvancedFields, - IBrowserSimpleFields, + ConfigKey, + BrowserAdvancedFields as BrowserAdvancedFieldsType, + BrowserSimpleFields, Validation, + DataStream, } from '../types'; import { BrowserAdvancedFieldsContextProvider, @@ -38,8 +38,8 @@ describe('', () => { defaultSimpleFields = defaultBrowserSimpleFields, validate = defaultValidation, }: { - defaultValues?: IBrowserAdvancedFields; - defaultSimpleFields?: IBrowserSimpleFields; + defaultValues?: BrowserAdvancedFieldsType; + defaultSimpleFields?: BrowserSimpleFields; validate?: Validation; }) => { return ( @@ -59,7 +59,7 @@ describe('', () => { const syntheticsArgs = getByLabelText('Synthetics args'); const screenshots = getByLabelText('Screenshot options') as HTMLInputElement; - expect(screenshots.value).toEqual(defaultConfig[ConfigKeys.SCREENSHOTS]); + expect(screenshots.value).toEqual(defaultConfig[ConfigKey.SCREENSHOTS]); expect(syntheticsArgs).toBeInTheDocument(); }); @@ -86,7 +86,7 @@ describe('', () => { ); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx index 0133642b8c4b37..1d62ea7fe40c60 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx @@ -20,7 +20,7 @@ import { ComboBox } from '../combo_box'; import { useBrowserAdvancedFieldsContext, useBrowserSimpleFieldsContext } from '../contexts'; -import { ConfigKeys, Validation, ScreenshotOption } from '../types'; +import { ConfigKey, Validation, ScreenshotOption } from '../types'; import { OptionalLabel } from '../optional_label'; import { ThrottlingFields } from './throttling_fields'; @@ -34,7 +34,7 @@ export const BrowserAdvancedFields = memo(({ validate }) => { const { fields: simpleFields } = useBrowserSimpleFieldsContext(); const handleInputChange = useCallback( - ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }, [setFields] @@ -47,7 +47,7 @@ export const BrowserAdvancedFields = memo(({ validate }) => { data-test-subj="syntheticsBrowserAdvancedFieldsAccordion" > - {simpleFields[ConfigKeys.SOURCE_ZIP_URL] && ( + {simpleFields[ConfigKey.SOURCE_ZIP_URL] && ( @@ -81,11 +81,11 @@ export const BrowserAdvancedFields = memo(({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.JOURNEY_FILTERS_MATCH, + configKey: ConfigKey.JOURNEY_FILTERS_MATCH, }) } data-test-subj="syntheticsBrowserJourneyFiltersMatch" @@ -107,9 +107,9 @@ export const BrowserAdvancedFields = memo(({ validate }) => { } > - handleInputChange({ value, configKey: ConfigKeys.JOURNEY_FILTERS_TAGS }) + handleInputChange({ value, configKey: ConfigKey.JOURNEY_FILTERS_TAGS }) } data-test-subj="syntheticsBrowserJourneyFiltersTags" /> @@ -146,7 +146,7 @@ export const BrowserAdvancedFields = memo(({ validate }) => { > (({ validate }) => { onChange={(event) => handleInputChange({ value: event.target.checked, - configKey: ConfigKeys.IGNORE_HTTPS_ERRORS, + configKey: ConfigKey.IGNORE_HTTPS_ERRORS, }) } /> @@ -179,11 +179,11 @@ export const BrowserAdvancedFields = memo(({ validate }) => { > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.SCREENSHOTS, + configKey: ConfigKey.SCREENSHOTS, }) } data-test-subj="syntheticsBrowserScreenshots" @@ -205,10 +205,8 @@ export const BrowserAdvancedFields = memo(({ validate }) => { } > - handleInputChange({ value, configKey: ConfigKeys.SYNTHETICS_ARGS }) - } + selectedOptions={fields[ConfigKey.SYNTHETICS_ARGS]} + onChange={(value) => handleInputChange({ value, configKey: ConfigKey.SYNTHETICS_ARGS })} data-test-subj="syntheticsBrowserSyntheticsArgs" />
diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts index 5384122a186218..20ff3d5081ba63 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { BrowserFields, ConfigKeys } from '../types'; +import { BrowserFields, ConfigKey } from '../types'; import { Formatter, commonFormatters, @@ -22,53 +22,52 @@ import { export type BrowserFormatMap = Record; const throttlingFormatter: Formatter = (fields) => { - if (!fields[ConfigKeys.IS_THROTTLING_ENABLED]) return 'false'; + if (!fields[ConfigKey.IS_THROTTLING_ENABLED]) return 'false'; const getThrottlingValue = (v: string | undefined, suffix: 'd' | 'u' | 'l') => v !== '' && v !== undefined ? `${v}${suffix}` : null; return [ - getThrottlingValue(fields[ConfigKeys.DOWNLOAD_SPEED], 'd'), - getThrottlingValue(fields[ConfigKeys.UPLOAD_SPEED], 'u'), - getThrottlingValue(fields[ConfigKeys.LATENCY], 'l'), + getThrottlingValue(fields[ConfigKey.DOWNLOAD_SPEED], 'd'), + getThrottlingValue(fields[ConfigKey.UPLOAD_SPEED], 'u'), + getThrottlingValue(fields[ConfigKey.LATENCY], 'l'), ] .filter((v) => v !== null) .join('/'); }; export const browserFormatters: BrowserFormatMap = { - [ConfigKeys.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKeys.METADATA]), - [ConfigKeys.SOURCE_ZIP_URL]: null, - [ConfigKeys.SOURCE_ZIP_USERNAME]: null, - [ConfigKeys.SOURCE_ZIP_PASSWORD]: null, - [ConfigKeys.SOURCE_ZIP_FOLDER]: null, - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: null, - [ConfigKeys.SOURCE_INLINE]: (fields) => stringToJsonFormatter(fields[ConfigKeys.SOURCE_INLINE]), - [ConfigKeys.PARAMS]: null, - [ConfigKeys.SCREENSHOTS]: null, - [ConfigKeys.IS_THROTTLING_ENABLED]: null, - [ConfigKeys.DOWNLOAD_SPEED]: null, - [ConfigKeys.UPLOAD_SPEED]: null, - [ConfigKeys.LATENCY]: null, - [ConfigKeys.SYNTHETICS_ARGS]: (fields) => - arrayToJsonFormatter(fields[ConfigKeys.SYNTHETICS_ARGS]), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: (fields) => - tlsValueToYamlFormatter(fields[ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: (fields) => - tlsValueToYamlFormatter(fields[ConfigKeys.ZIP_URL_TLS_CERTIFICATE]), - [ConfigKeys.ZIP_URL_TLS_KEY]: (fields) => - tlsValueToYamlFormatter(fields[ConfigKeys.ZIP_URL_TLS_KEY]), - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: (fields) => - tlsValueToStringFormatter(fields[ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]), - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: (fields) => - tlsValueToStringFormatter(fields[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]), - [ConfigKeys.ZIP_URL_TLS_VERSION]: (fields) => - tlsArrayToYamlFormatter(fields[ConfigKeys.ZIP_URL_TLS_VERSION]), - [ConfigKeys.JOURNEY_FILTERS_MATCH]: (fields) => - stringToJsonFormatter(fields[ConfigKeys.JOURNEY_FILTERS_MATCH]), - [ConfigKeys.JOURNEY_FILTERS_TAGS]: (fields) => - arrayToJsonFormatter(fields[ConfigKeys.JOURNEY_FILTERS_TAGS]), - [ConfigKeys.THROTTLING_CONFIG]: throttlingFormatter, - [ConfigKeys.IGNORE_HTTPS_ERRORS]: null, + [ConfigKey.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKey.METADATA]), + [ConfigKey.SOURCE_ZIP_URL]: null, + [ConfigKey.SOURCE_ZIP_USERNAME]: null, + [ConfigKey.SOURCE_ZIP_PASSWORD]: null, + [ConfigKey.SOURCE_ZIP_FOLDER]: null, + [ConfigKey.SOURCE_ZIP_PROXY_URL]: null, + [ConfigKey.SOURCE_INLINE]: (fields) => stringToJsonFormatter(fields[ConfigKey.SOURCE_INLINE]), + [ConfigKey.PARAMS]: null, + [ConfigKey.SCREENSHOTS]: null, + [ConfigKey.IS_THROTTLING_ENABLED]: null, + [ConfigKey.DOWNLOAD_SPEED]: null, + [ConfigKey.UPLOAD_SPEED]: null, + [ConfigKey.LATENCY]: null, + [ConfigKey.SYNTHETICS_ARGS]: (fields) => arrayToJsonFormatter(fields[ConfigKey.SYNTHETICS_ARGS]), + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: (fields) => + tlsValueToYamlFormatter(fields[ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]), + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: (fields) => + tlsValueToYamlFormatter(fields[ConfigKey.ZIP_URL_TLS_CERTIFICATE]), + [ConfigKey.ZIP_URL_TLS_KEY]: (fields) => + tlsValueToYamlFormatter(fields[ConfigKey.ZIP_URL_TLS_KEY]), + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: (fields) => + tlsValueToStringFormatter(fields[ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]), + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: (fields) => + tlsValueToStringFormatter(fields[ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]), + [ConfigKey.ZIP_URL_TLS_VERSION]: (fields) => + tlsArrayToYamlFormatter(fields[ConfigKey.ZIP_URL_TLS_VERSION]), + [ConfigKey.JOURNEY_FILTERS_MATCH]: (fields) => + stringToJsonFormatter(fields[ConfigKey.JOURNEY_FILTERS_MATCH]), + [ConfigKey.JOURNEY_FILTERS_TAGS]: (fields) => + arrayToJsonFormatter(fields[ConfigKey.JOURNEY_FILTERS_TAGS]), + [ConfigKey.THROTTLING_CONFIG]: throttlingFormatter, + [ConfigKey.IGNORE_HTTPS_ERRORS]: null, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts index 49a08a12454e36..9b70f5bba6e24e 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.test.ts @@ -5,62 +5,62 @@ * 2.0. */ -import { ConfigKeys } from '../types'; +import { ConfigKey } from '../types'; import { getThrottlingParamNormalizer, isThrottlingEnabledNormalizer } from './normalizers'; import { defaultBrowserAdvancedFields } from '../contexts'; describe('browser normalizers', () => { const makeThrottlingConfig = (value: string) => ({ - [ConfigKeys.THROTTLING_CONFIG]: { value }, + [ConfigKey.THROTTLING_CONFIG]: { value }, }); describe('throttlingToParameterNormalizer', () => { it('can extract download values', () => { const fields = makeThrottlingConfig('10d/5u/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(fields)).toEqual('10'); + expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(fields)).toEqual('10'); }); it('can extract upload values', () => { const fields = makeThrottlingConfig('10d/5u/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(fields)).toEqual('5'); + expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(fields)).toEqual('5'); }); it('can extract latency values', () => { const fields = makeThrottlingConfig('10d/5u/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(fields)).toEqual('2.5'); + expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(fields)).toEqual('2.5'); }); it('returns default values when throttling is disabled', () => { const fields = makeThrottlingConfig('false'); - expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.DOWNLOAD_SPEED] + expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.DOWNLOAD_SPEED] ); - expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.UPLOAD_SPEED] + expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.UPLOAD_SPEED] ); - expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(fields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.LATENCY] + expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(fields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.LATENCY] ); }); it("returns default values when the desired suffix doesn't exist", () => { const noUploadFields = makeThrottlingConfig('10d/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED)(noUploadFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.UPLOAD_SPEED] + expect(getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED)(noUploadFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.UPLOAD_SPEED] ); const noDownloadFields = makeThrottlingConfig('10u/2.5l'); - expect(getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED)(noDownloadFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.DOWNLOAD_SPEED] + expect(getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED)(noDownloadFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.DOWNLOAD_SPEED] ); const noLatencyFields = makeThrottlingConfig('10d/5u'); - expect(getThrottlingParamNormalizer(ConfigKeys.LATENCY)(noLatencyFields)).toEqual( - defaultBrowserAdvancedFields[ConfigKeys.LATENCY] + expect(getThrottlingParamNormalizer(ConfigKey.LATENCY)(noLatencyFields)).toEqual( + defaultBrowserAdvancedFields[ConfigKey.LATENCY] ); }); }); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts index 6d8b35673fba36..71d178494cff1f 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts @@ -7,7 +7,7 @@ import { BrowserFields, - ConfigKeys, + ConfigKey, ThrottlingSuffix, ThrottlingConfigKey, configKeyToThrottlingSuffix, @@ -28,11 +28,11 @@ const defaultBrowserFields = { ...defaultBrowserAdvancedFields, }; -export const getBrowserNormalizer = (key: ConfigKeys) => { +export const getBrowserNormalizer = (key: ConfigKey) => { return getNormalizer(key, defaultBrowserFields); }; -export const getBrowserJsonToJavascriptNormalizer = (key: ConfigKeys) => { +export const getBrowserJsonToJavascriptNormalizer = (key: ConfigKey) => { return getJsonToJavascriptNormalizer(key, defaultBrowserFields); }; @@ -52,7 +52,7 @@ export function throttlingToParameterNormalizer( export const isThrottlingEnabledNormalizer: Normalizer = function isThrottlingEnabledNormalizer( fields ) { - const throttlingEnabled = fields?.[ConfigKeys.THROTTLING_CONFIG]?.value; + const throttlingEnabled = fields?.[ConfigKey.THROTTLING_CONFIG]?.value; // If we have any value that's not an explicit "false" it means throttling is "on" return throttlingEnabled !== 'false'; @@ -61,48 +61,48 @@ export const isThrottlingEnabledNormalizer: Normalizer = function isThrottlingEn export function getThrottlingParamNormalizer(key: ThrottlingConfigKey): Normalizer { const paramSuffix = configKeyToThrottlingSuffix[key]; return (fields) => - throttlingToParameterNormalizer(paramSuffix, fields?.[ConfigKeys.THROTTLING_CONFIG]?.value) ?? + throttlingToParameterNormalizer(paramSuffix, fields?.[ConfigKey.THROTTLING_CONFIG]?.value) ?? defaultBrowserFields[key]; } export const browserNormalizers: BrowserNormalizerMap = { - [ConfigKeys.METADATA]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.METADATA), - [ConfigKeys.SOURCE_ZIP_URL]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_URL), - [ConfigKeys.SOURCE_ZIP_USERNAME]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_USERNAME), - [ConfigKeys.SOURCE_ZIP_PASSWORD]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_PASSWORD), - [ConfigKeys.SOURCE_ZIP_FOLDER]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_FOLDER), - [ConfigKeys.SOURCE_INLINE]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.SOURCE_INLINE), - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: getBrowserNormalizer(ConfigKeys.SOURCE_ZIP_PROXY_URL), - [ConfigKeys.PARAMS]: getBrowserNormalizer(ConfigKeys.PARAMS), - [ConfigKeys.SCREENSHOTS]: getBrowserNormalizer(ConfigKeys.SCREENSHOTS), - [ConfigKeys.SYNTHETICS_ARGS]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.SYNTHETICS_ARGS), - [ConfigKeys.IS_THROTTLING_ENABLED]: isThrottlingEnabledNormalizer, - [ConfigKeys.DOWNLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKeys.DOWNLOAD_SPEED), - [ConfigKeys.UPLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKeys.UPLOAD_SPEED), - [ConfigKeys.LATENCY]: getThrottlingParamNormalizer(ConfigKeys.LATENCY), - [ConfigKeys.THROTTLING_CONFIG]: getBrowserNormalizer(ConfigKeys.THROTTLING_CONFIG), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: getBrowserJsonToJavascriptNormalizer( - ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES + [ConfigKey.METADATA]: getBrowserJsonToJavascriptNormalizer(ConfigKey.METADATA), + [ConfigKey.SOURCE_ZIP_URL]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_URL), + [ConfigKey.SOURCE_ZIP_USERNAME]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_USERNAME), + [ConfigKey.SOURCE_ZIP_PASSWORD]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_PASSWORD), + [ConfigKey.SOURCE_ZIP_FOLDER]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_FOLDER), + [ConfigKey.SOURCE_INLINE]: getBrowserJsonToJavascriptNormalizer(ConfigKey.SOURCE_INLINE), + [ConfigKey.SOURCE_ZIP_PROXY_URL]: getBrowserNormalizer(ConfigKey.SOURCE_ZIP_PROXY_URL), + [ConfigKey.PARAMS]: getBrowserNormalizer(ConfigKey.PARAMS), + [ConfigKey.SCREENSHOTS]: getBrowserNormalizer(ConfigKey.SCREENSHOTS), + [ConfigKey.SYNTHETICS_ARGS]: getBrowserJsonToJavascriptNormalizer(ConfigKey.SYNTHETICS_ARGS), + [ConfigKey.IS_THROTTLING_ENABLED]: isThrottlingEnabledNormalizer, + [ConfigKey.DOWNLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKey.DOWNLOAD_SPEED), + [ConfigKey.UPLOAD_SPEED]: getThrottlingParamNormalizer(ConfigKey.UPLOAD_SPEED), + [ConfigKey.LATENCY]: getThrottlingParamNormalizer(ConfigKey.LATENCY), + [ConfigKey.THROTTLING_CONFIG]: getBrowserNormalizer(ConfigKey.THROTTLING_CONFIG), + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: getBrowserJsonToJavascriptNormalizer( + ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES ), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: getBrowserJsonToJavascriptNormalizer( - ConfigKeys.ZIP_URL_TLS_CERTIFICATE + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: getBrowserJsonToJavascriptNormalizer( + ConfigKey.ZIP_URL_TLS_CERTIFICATE ), - [ConfigKeys.ZIP_URL_TLS_KEY]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.ZIP_URL_TLS_KEY), - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: getBrowserNormalizer( - ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE + [ConfigKey.ZIP_URL_TLS_KEY]: getBrowserJsonToJavascriptNormalizer(ConfigKey.ZIP_URL_TLS_KEY), + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: getBrowserNormalizer( + ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE ), - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: getBrowserNormalizer( - ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: getBrowserNormalizer( + ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE ), - [ConfigKeys.ZIP_URL_TLS_VERSION]: getBrowserJsonToJavascriptNormalizer( - ConfigKeys.ZIP_URL_TLS_VERSION + [ConfigKey.ZIP_URL_TLS_VERSION]: getBrowserJsonToJavascriptNormalizer( + ConfigKey.ZIP_URL_TLS_VERSION ), - [ConfigKeys.JOURNEY_FILTERS_MATCH]: getBrowserJsonToJavascriptNormalizer( - ConfigKeys.JOURNEY_FILTERS_MATCH + [ConfigKey.JOURNEY_FILTERS_MATCH]: getBrowserJsonToJavascriptNormalizer( + ConfigKey.JOURNEY_FILTERS_MATCH ), - [ConfigKeys.JOURNEY_FILTERS_TAGS]: getBrowserJsonToJavascriptNormalizer( - ConfigKeys.JOURNEY_FILTERS_TAGS + [ConfigKey.JOURNEY_FILTERS_TAGS]: getBrowserJsonToJavascriptNormalizer( + ConfigKey.JOURNEY_FILTERS_TAGS ), - [ConfigKeys.IGNORE_HTTPS_ERRORS]: getBrowserNormalizer(ConfigKeys.IGNORE_HTTPS_ERRORS), + [ConfigKey.IGNORE_HTTPS_ERRORS]: getBrowserNormalizer(ConfigKey.IGNORE_HTTPS_ERRORS), ...commonNormalizers, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx index ae58df0e058afc..b0f3b295992423 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx @@ -8,7 +8,8 @@ import React, { memo, useMemo, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow } from '@elastic/eui'; -import { ConfigKeys, Validation } from '../types'; +import { Validation } from '../types'; +import { ConfigKey } from '../types'; import { useBrowserSimpleFieldsContext } from '../contexts'; import { ScheduleField } from '../schedule_field'; import { SourceField } from './source_field'; @@ -20,7 +21,7 @@ interface Props { export const BrowserSimpleFields = memo(({ validate }) => { const { fields, setFields, defaultValues } = useBrowserSimpleFieldsContext(); - const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }; const onChangeSourceField = useCallback( @@ -37,15 +38,15 @@ export const BrowserSimpleFields = memo(({ validate }) => { }) => { setFields((prevFields) => ({ ...prevFields, - [ConfigKeys.SOURCE_ZIP_URL]: zipUrl, - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: proxyUrl, - [ConfigKeys.SOURCE_ZIP_FOLDER]: folder, - [ConfigKeys.SOURCE_ZIP_USERNAME]: username, - [ConfigKeys.SOURCE_ZIP_PASSWORD]: password, - [ConfigKeys.SOURCE_INLINE]: inlineScript, - [ConfigKeys.PARAMS]: params, - [ConfigKeys.METADATA]: { - ...prevFields[ConfigKeys.METADATA], + [ConfigKey.SOURCE_ZIP_URL]: zipUrl, + [ConfigKey.SOURCE_ZIP_PROXY_URL]: proxyUrl, + [ConfigKey.SOURCE_ZIP_FOLDER]: folder, + [ConfigKey.SOURCE_ZIP_USERNAME]: username, + [ConfigKey.SOURCE_ZIP_PASSWORD]: password, + [ConfigKey.SOURCE_INLINE]: inlineScript, + [ConfigKey.PARAMS]: params, + [ConfigKey.METADATA]: { + ...prevFields[ConfigKey.METADATA], script_source: { is_generated_script: isGeneratedScript, file_name: fileName, @@ -66,7 +67,7 @@ export const BrowserSimpleFields = memo(({ validate }) => { defaultMessage="Monitor interval" /> } - isInvalid={!!validate[ConfigKeys.SCHEDULE]?.(fields)} + isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ (({ validate }) => { onChange={(schedule) => handleInputChange({ value: schedule, - configKey: ConfigKeys.SCHEDULE, + configKey: ConfigKey.SCHEDULE, }) } - number={fields[ConfigKeys.SCHEDULE].number} - unit={fields[ConfigKeys.SCHEDULE].unit} + number={fields[ConfigKey.SCHEDULE].number} + unit={fields[ConfigKey.SCHEDULE].unit} /> (({ validate }) => { onChange={onChangeSourceField} defaultConfig={useMemo( () => ({ - zipUrl: defaultValues[ConfigKeys.SOURCE_ZIP_URL], - proxyUrl: defaultValues[ConfigKeys.SOURCE_ZIP_PROXY_URL], - folder: defaultValues[ConfigKeys.SOURCE_ZIP_FOLDER], - username: defaultValues[ConfigKeys.SOURCE_ZIP_USERNAME], - password: defaultValues[ConfigKeys.SOURCE_ZIP_PASSWORD], - inlineScript: defaultValues[ConfigKeys.SOURCE_INLINE], - params: defaultValues[ConfigKeys.PARAMS], + zipUrl: defaultValues[ConfigKey.SOURCE_ZIP_URL], + proxyUrl: defaultValues[ConfigKey.SOURCE_ZIP_PROXY_URL], + folder: defaultValues[ConfigKey.SOURCE_ZIP_FOLDER], + username: defaultValues[ConfigKey.SOURCE_ZIP_USERNAME], + password: defaultValues[ConfigKey.SOURCE_ZIP_PASSWORD], + inlineScript: defaultValues[ConfigKey.SOURCE_INLINE], + params: defaultValues[ConfigKey.PARAMS], isGeneratedScript: - defaultValues[ConfigKeys.METADATA].script_source?.is_generated_script, - fileName: defaultValues[ConfigKeys.METADATA].script_source?.file_name, + defaultValues[ConfigKey.METADATA].script_source?.is_generated_script, + fileName: defaultValues[ConfigKey.METADATA].script_source?.file_name, }), [defaultValues] )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx index b27e848c98a139..8021253c80e997 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; import { ThrottlingFields } from './throttling_fields'; -import { DataStream, IBrowserAdvancedFields, IBrowserSimpleFields, Validation } from '../types'; +import { DataStream, BrowserAdvancedFields, BrowserSimpleFields, Validation } from '../types'; import { BrowserAdvancedFieldsContextProvider, BrowserSimpleFieldsContextProvider, @@ -32,8 +32,8 @@ describe('', () => { defaultSimpleFields = defaultBrowserSimpleFields, validate = defaultValidation, }: { - defaultValues?: IBrowserAdvancedFields; - defaultSimpleFields?: IBrowserSimpleFields; + defaultValues?: BrowserAdvancedFields; + defaultSimpleFields?: BrowserSimpleFields; validate?: Validation; }) => { return ( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx index 19bfd961f64616..8648b531ef3a85 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx @@ -18,17 +18,17 @@ import { import { OptionalLabel } from '../optional_label'; import { useBrowserAdvancedFieldsContext } from '../contexts'; -import { Validation, ConfigKeys } from '../types'; +import { Validation, ConfigKey } from '../types'; interface Props { validate: Validation; } type ThrottlingConfigs = - | ConfigKeys.IS_THROTTLING_ENABLED - | ConfigKeys.DOWNLOAD_SPEED - | ConfigKeys.UPLOAD_SPEED - | ConfigKeys.LATENCY; + | ConfigKey.IS_THROTTLING_ENABLED + | ConfigKey.DOWNLOAD_SPEED + | ConfigKey.UPLOAD_SPEED + | ConfigKey.LATENCY; export const ThrottlingFields = memo(({ validate }) => { const { fields, setFields } = useBrowserAdvancedFieldsContext(); @@ -40,7 +40,7 @@ export const ThrottlingFields = memo(({ validate }) => { [setFields] ); - const throttlingInputs = fields[ConfigKeys.IS_THROTTLING_ENABLED] ? ( + const throttlingInputs = fields[ConfigKey.IS_THROTTLING_ENABLED] ? ( <> (({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.DOWNLOAD_SPEED]?.(fields)} + isInvalid={!!validate[ConfigKey.DOWNLOAD_SPEED]?.(fields)} error={ (({ validate }) => { { handleInputChange({ value: event.target.value, - configKey: ConfigKeys.DOWNLOAD_SPEED, + configKey: ConfigKey.DOWNLOAD_SPEED, }); }} data-test-subj="syntheticsBrowserDownloadSpeed" @@ -85,7 +85,7 @@ export const ThrottlingFields = memo(({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.UPLOAD_SPEED]?.(fields)} + isInvalid={!!validate[ConfigKey.UPLOAD_SPEED]?.(fields)} error={ (({ validate }) => { handleInputChange({ value: event.target.value, - configKey: ConfigKeys.UPLOAD_SPEED, + configKey: ConfigKey.UPLOAD_SPEED, }) } data-test-subj="syntheticsBrowserUploadSpeed" @@ -119,7 +119,7 @@ export const ThrottlingFields = memo(({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.LATENCY]?.(fields)} + isInvalid={!!validate[ConfigKey.LATENCY]?.(fields)} data-test-subj="syntheticsBrowserLatency" error={ (({ validate }) => { > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.LATENCY, + configKey: ConfigKey.LATENCY, }) } append={ @@ -168,7 +168,7 @@ export const ThrottlingFields = memo(({ validate }) => { id={'uptimeFleetIsThrottlingEnabled'} aria-label="enable throttling configuration" data-test-subj="syntheticsBrowserIsThrottlingEnabled" - checked={fields[ConfigKeys.IS_THROTTLING_ENABLED]} + checked={fields[ConfigKey.IS_THROTTLING_ENABLED]} label={ (({ validate }) => { onChange={(event) => handleInputChange({ value: event.target.checked, - configKey: ConfigKeys.IS_THROTTLING_ENABLED, + configKey: ConfigKey.IS_THROTTLING_ENABLED, }) } /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx index 334166e716ff3d..9be1d3746899f8 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; import { ZipUrlTLSFields } from './zip_url_tls_fields'; -import { ConfigKeys, VerificationMode } from '../types'; +import { ConfigKey, VerificationMode } from '../types'; import { BrowserSimpleFieldsContextProvider, PolicyConfigContextProvider, @@ -57,31 +57,31 @@ describe('', () => { const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; const newValues = { - [ConfigKeys.TLS_CERTIFICATE]: 'sampleClientCertificate', - [ConfigKeys.TLS_KEY]: 'sampleClientKey', - [ConfigKeys.TLS_KEY_PASSPHRASE]: 'sampleClientKeyPassphrase', - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: 'sampleCertificateAuthorities', - [ConfigKeys.TLS_VERIFICATION_MODE]: VerificationMode.NONE, + [ConfigKey.TLS_CERTIFICATE]: 'sampleClientCertificate', + [ConfigKey.TLS_KEY]: 'sampleClientKey', + [ConfigKey.TLS_KEY_PASSPHRASE]: 'sampleClientKeyPassphrase', + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'sampleCertificateAuthorities', + [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.NONE, }; fireEvent.change(clientCertificate, { - target: { value: newValues[ConfigKeys.TLS_CERTIFICATE] }, + target: { value: newValues[ConfigKey.TLS_CERTIFICATE] }, }); - fireEvent.change(clientKey, { target: { value: newValues[ConfigKeys.TLS_KEY] } }); + fireEvent.change(clientKey, { target: { value: newValues[ConfigKey.TLS_KEY] } }); fireEvent.change(clientKeyPassphrase, { - target: { value: newValues[ConfigKeys.TLS_KEY_PASSPHRASE] }, + target: { value: newValues[ConfigKey.TLS_KEY_PASSPHRASE] }, }); fireEvent.change(certificateAuthorities, { - target: { value: newValues[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES] }, + target: { value: newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES] }, }); fireEvent.change(verificationMode, { - target: { value: newValues[ConfigKeys.TLS_VERIFICATION_MODE] }, + target: { value: newValues[ConfigKey.TLS_VERIFICATION_MODE] }, }); - expect(clientCertificate.value).toEqual(newValues[ConfigKeys.TLS_CERTIFICATE]); - expect(clientKey.value).toEqual(newValues[ConfigKeys.TLS_KEY]); - expect(certificateAuthorities.value).toEqual(newValues[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]); - expect(verificationMode.value).toEqual(newValues[ConfigKeys.TLS_VERIFICATION_MODE]); + expect(clientCertificate.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE]); + expect(clientKey.value).toEqual(newValues[ConfigKey.TLS_KEY]); + expect(certificateAuthorities.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]); + expect(verificationMode.value).toEqual(newValues[ConfigKey.TLS_VERIFICATION_MODE]); }); it('shows warning when verification mode is set to none', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx index caa58755044414..590c5d4f3abc58 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx @@ -18,7 +18,7 @@ import { defaultTLSFields, } from '../contexts'; -import { ConfigKeys } from '../types'; +import { ConfigKey } from '../types'; export const ZipUrlTLSFields = () => { const { defaultValues, setFields } = useBrowserSimpleFieldsContext(); @@ -28,12 +28,12 @@ export const ZipUrlTLSFields = () => { (tlsConfig: TLSConfig) => { setFields((prevFields) => ({ ...prevFields, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: tlsConfig.certificateAuthorities, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: tlsConfig.certificate, - [ConfigKeys.ZIP_URL_TLS_KEY]: tlsConfig.key, - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: tlsConfig.keyPassphrase, - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: tlsConfig.verificationMode, - [ConfigKeys.ZIP_URL_TLS_VERSION]: tlsConfig.version, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: tlsConfig.certificateAuthorities, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: tlsConfig.certificate, + [ConfigKey.ZIP_URL_TLS_KEY]: tlsConfig.key, + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: tlsConfig.keyPassphrase, + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: tlsConfig.verificationMode, + [ConfigKey.ZIP_URL_TLS_VERSION]: tlsConfig.version, })); }, [setFields] @@ -43,12 +43,12 @@ export const ZipUrlTLSFields = () => { if (!isZipUrlTLSEnabled) { setFields((prevFields) => ({ ...prevFields, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: undefined, - [ConfigKeys.ZIP_URL_TLS_KEY]: undefined, - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, - [ConfigKeys.ZIP_URL_TLS_VERSION]: undefined, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: undefined, + [ConfigKey.ZIP_URL_TLS_KEY]: undefined, + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, + [ConfigKey.ZIP_URL_TLS_VERSION]: undefined, })); } }, [setFields, isZipUrlTLSEnabled]); @@ -72,22 +72,21 @@ export const ZipUrlTLSFields = () => { void; + fields: CommonFieldsType; + onChange: ({ value, configKey }: { value: string | string[]; configKey: ConfigKey }) => void; } export function CommonFields({ fields, onChange, validate }: Props) { @@ -37,11 +38,11 @@ export function CommonFields({ fields, onChange, validate }: Props) { } > onChange({ value: event.target.value, - configKey: ConfigKeys.APM_SERVICE_NAME, + configKey: ConfigKey.APM_SERVICE_NAME, }) } data-test-subj="syntheticsAPMServiceName" @@ -54,9 +55,9 @@ export function CommonFields({ fields, onChange, validate }: Props) { defaultMessage="Timeout in seconds" /> } - isInvalid={!!validate[ConfigKeys.TIMEOUT]?.(fields)} + isInvalid={!!validate[ConfigKey.TIMEOUT]?.(fields)} error={ - parseInt(fields[ConfigKeys.TIMEOUT], 10) < 0 ? ( + parseInt(fields[ConfigKey.TIMEOUT], 10) < 0 ? ( onChange({ value: event.target.value, - configKey: ConfigKeys.TIMEOUT, + configKey: ConfigKey.TIMEOUT, }) } step={'any'} @@ -103,8 +104,8 @@ export function CommonFields({ fields, onChange, validate }: Props) { } > onChange({ value, configKey: ConfigKeys.TAGS })} + selectedOptions={fields[ConfigKey.TAGS]} + onChange={(value) => onChange({ value, configKey: ConfigKey.TAGS })} data-test-subj="syntheticsTags" /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/default_values.ts b/x-pack/plugins/uptime/public/components/fleet_package/common/default_values.ts index bba8cefd749ee3..50c540266724ad 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/default_values.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/default_values.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { ICommonFields, ConfigKeys, ScheduleUnit, DataStream } from '../types'; +import { CommonFields, ConfigKey, ScheduleUnit, DataStream } from '../types'; -export const defaultValues: ICommonFields = { - [ConfigKeys.MONITOR_TYPE]: DataStream.HTTP, - [ConfigKeys.SCHEDULE]: { +export const defaultValues: CommonFields = { + [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.SCHEDULE]: { number: '3', unit: ScheduleUnit.MINUTES, }, - [ConfigKeys.APM_SERVICE_NAME]: '', - [ConfigKeys.TAGS]: [], - [ConfigKeys.TIMEOUT]: '16', + [ConfigKey.APM_SERVICE_NAME]: '', + [ConfigKey.TAGS]: [], + [ConfigKey.TIMEOUT]: '16', }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/common/formatters.ts index 278aa1b03bab1a..077e0fb0becda6 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/formatters.ts @@ -5,22 +5,22 @@ * 2.0. */ -import { ICommonFields, ICustomFields, ConfigKeys } from '../types'; +import { CommonFields, MonitorFields, ConfigKey } from '../types'; -export type Formatter = null | ((fields: Partial) => string | null); +export type Formatter = null | ((fields: Partial) => string | null); -export type CommonFormatMap = Record; +export type CommonFormatMap = Record; export const commonFormatters: CommonFormatMap = { - [ConfigKeys.NAME]: null, - [ConfigKeys.MONITOR_TYPE]: null, - [ConfigKeys.SCHEDULE]: (fields) => + [ConfigKey.NAME]: null, + [ConfigKey.MONITOR_TYPE]: null, + [ConfigKey.SCHEDULE]: (fields) => JSON.stringify( - `@every ${fields[ConfigKeys.SCHEDULE]?.number}${fields[ConfigKeys.SCHEDULE]?.unit}` + `@every ${fields[ConfigKey.SCHEDULE]?.number}${fields[ConfigKey.SCHEDULE]?.unit}` ), - [ConfigKeys.APM_SERVICE_NAME]: null, - [ConfigKeys.TAGS]: (fields) => arrayToJsonFormatter(fields[ConfigKeys.TAGS]), - [ConfigKeys.TIMEOUT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.TIMEOUT]), + [ConfigKey.APM_SERVICE_NAME]: null, + [ConfigKey.TAGS]: (fields) => arrayToJsonFormatter(fields[ConfigKey.TAGS]), + [ConfigKey.TIMEOUT]: (fields) => secondsToCronFormatter(fields[ConfigKey.TIMEOUT]), }; export const arrayToJsonFormatter = (value: string[] = []) => diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/common/normalizers.ts index 57c4904d711c7a..5710a99a656606 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/normalizers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ICommonFields, ConfigKeys } from '../types'; +import { CommonFields, ConfigKey } from '../types'; import { NewPackagePolicyInput } from '../../../../../fleet/common'; import { defaultValues as commonDefaultValues } from './default_values'; @@ -13,7 +13,7 @@ import { defaultValues as commonDefaultValues } from './default_values'; export type Normalizer = (fields: NewPackagePolicyInput['vars']) => unknown; // create a type of all the common policy fields, as well as the fleet managed 'name' field -export type CommonNormalizerMap = Record; +export type CommonNormalizerMap = Record; /** * Takes a cron formatted seconds and returns just the number of seconds. Assumes that cron is already in seconds format. @@ -43,25 +43,25 @@ export function getCronNormalizer(key: string, defaultValues: Fields): N cronToSecondsNormalizer(fields?.[key]?.value) ?? defaultValues[key as keyof Fields]; } -export const getCommonNormalizer = (key: ConfigKeys) => { +export const getCommonNormalizer = (key: ConfigKey) => { return getNormalizer(key, commonDefaultValues); }; -export const getCommonjsonToJavascriptNormalizer = (key: ConfigKeys) => { +export const getCommonjsonToJavascriptNormalizer = (key: ConfigKey) => { return getJsonToJavascriptNormalizer(key, commonDefaultValues); }; -export const getCommonCronToSecondsNormalizer = (key: ConfigKeys) => { +export const getCommonCronToSecondsNormalizer = (key: ConfigKey) => { return getCronNormalizer(key, commonDefaultValues); }; export const commonNormalizers: CommonNormalizerMap = { - [ConfigKeys.NAME]: (fields) => fields?.[ConfigKeys.NAME]?.value ?? '', - [ConfigKeys.MONITOR_TYPE]: getCommonNormalizer(ConfigKeys.MONITOR_TYPE), - [ConfigKeys.SCHEDULE]: (fields) => { - const value = fields?.[ConfigKeys.SCHEDULE]?.value; + [ConfigKey.NAME]: (fields) => fields?.[ConfigKey.NAME]?.value ?? '', + [ConfigKey.MONITOR_TYPE]: getCommonNormalizer(ConfigKey.MONITOR_TYPE), + [ConfigKey.SCHEDULE]: (fields) => { + const value = fields?.[ConfigKey.SCHEDULE]?.value; if (value) { - const fullString = JSON.parse(fields?.[ConfigKeys.SCHEDULE]?.value); + const fullString = JSON.parse(fields?.[ConfigKey.SCHEDULE]?.value); const fullSchedule = fullString.replace('@every ', ''); const unit = fullSchedule.slice(-1); const number = fullSchedule.slice(0, fullSchedule.length - 1); @@ -70,10 +70,10 @@ export const commonNormalizers: CommonNormalizerMap = { number, }; } else { - return commonDefaultValues[ConfigKeys.SCHEDULE]; + return commonDefaultValues[ConfigKey.SCHEDULE]; } }, - [ConfigKeys.APM_SERVICE_NAME]: getCommonNormalizer(ConfigKeys.APM_SERVICE_NAME), - [ConfigKeys.TAGS]: getCommonjsonToJavascriptNormalizer(ConfigKeys.TAGS), - [ConfigKeys.TIMEOUT]: getCommonCronToSecondsNormalizer(ConfigKeys.TIMEOUT), + [ConfigKey.APM_SERVICE_NAME]: getCommonNormalizer(ConfigKey.APM_SERVICE_NAME), + [ConfigKey.TAGS]: getCommonjsonToJavascriptNormalizer(ConfigKey.TAGS), + [ConfigKey.TIMEOUT]: getCommonCronToSecondsNormalizer(ConfigKey.TIMEOUT), }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context.tsx index 82b8a77d621c7a..c16a9639b3127c 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context.tsx @@ -6,47 +6,47 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { IBrowserSimpleFields, ConfigKeys, DataStream } from '../types'; +import { BrowserSimpleFields, ConfigKey, DataStream } from '../types'; import { defaultValues as commonDefaultValues } from '../common/default_values'; -interface IBrowserSimpleFieldsContext { - setFields: React.Dispatch>; - fields: IBrowserSimpleFields; - defaultValues: IBrowserSimpleFields; +interface BrowserSimpleFieldsContext { + setFields: React.Dispatch>; + fields: BrowserSimpleFields; + defaultValues: BrowserSimpleFields; } -interface IBrowserSimpleFieldsContextProvider { +interface BrowserSimpleFieldsContextProvider { children: React.ReactNode; - defaultValues?: IBrowserSimpleFields; + defaultValues?: BrowserSimpleFields; } -export const initialValues: IBrowserSimpleFields = { +export const initialValues: BrowserSimpleFields = { ...commonDefaultValues, - [ConfigKeys.METADATA]: { + [ConfigKey.METADATA]: { script_source: { is_generated_script: false, file_name: '', }, is_zip_url_tls_enabled: false, }, - [ConfigKeys.MONITOR_TYPE]: DataStream.BROWSER, - [ConfigKeys.SOURCE_ZIP_URL]: '', - [ConfigKeys.SOURCE_ZIP_USERNAME]: '', - [ConfigKeys.SOURCE_ZIP_PASSWORD]: '', - [ConfigKeys.SOURCE_ZIP_FOLDER]: '', - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: '', - [ConfigKeys.SOURCE_INLINE]: '', - [ConfigKeys.PARAMS]: '', - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: undefined, - [ConfigKeys.ZIP_URL_TLS_KEY]: undefined, - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, - [ConfigKeys.ZIP_URL_TLS_VERSION]: undefined, + [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.SOURCE_ZIP_URL]: '', + [ConfigKey.SOURCE_ZIP_USERNAME]: '', + [ConfigKey.SOURCE_ZIP_PASSWORD]: '', + [ConfigKey.SOURCE_ZIP_FOLDER]: '', + [ConfigKey.SOURCE_ZIP_PROXY_URL]: '', + [ConfigKey.SOURCE_INLINE]: '', + [ConfigKey.PARAMS]: '', + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: undefined, + [ConfigKey.ZIP_URL_TLS_KEY]: undefined, + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, + [ConfigKey.ZIP_URL_TLS_VERSION]: undefined, }; -const defaultContext: IBrowserSimpleFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: BrowserSimpleFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error( 'setFields was not initialized for Browser Simple Fields, set it when you invoke the context' ); @@ -60,8 +60,8 @@ export const BrowserSimpleFieldsContext = createContext(defaultContext); export const BrowserSimpleFieldsContextProvider = ({ children, defaultValues = initialValues, -}: IBrowserSimpleFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: BrowserSimpleFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx index 7dd279b1767618..3ddd7953a63109 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_context_advanced.tsx @@ -6,34 +6,34 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { IBrowserAdvancedFields, ConfigKeys, ScreenshotOption } from '../types'; +import { BrowserAdvancedFields, ConfigKey, ScreenshotOption } from '../types'; -interface IBrowserAdvancedFieldsContext { - setFields: React.Dispatch>; - fields: IBrowserAdvancedFields; - defaultValues: IBrowserAdvancedFields; +interface BrowserAdvancedFieldsContext { + setFields: React.Dispatch>; + fields: BrowserAdvancedFields; + defaultValues: BrowserAdvancedFields; } -interface IBrowserAdvancedFieldsContextProvider { +interface BrowserAdvancedFieldsContextProvider { children: React.ReactNode; - defaultValues?: IBrowserAdvancedFields; + defaultValues?: BrowserAdvancedFields; } -export const initialValues: IBrowserAdvancedFields = { - [ConfigKeys.SCREENSHOTS]: ScreenshotOption.ON, - [ConfigKeys.SYNTHETICS_ARGS]: [], - [ConfigKeys.JOURNEY_FILTERS_MATCH]: '', - [ConfigKeys.JOURNEY_FILTERS_TAGS]: [], - [ConfigKeys.IGNORE_HTTPS_ERRORS]: false, - [ConfigKeys.IS_THROTTLING_ENABLED]: true, - [ConfigKeys.DOWNLOAD_SPEED]: '5', - [ConfigKeys.UPLOAD_SPEED]: '3', - [ConfigKeys.LATENCY]: '20', - [ConfigKeys.THROTTLING_CONFIG]: '5d/3u/20l', +export const initialValues: BrowserAdvancedFields = { + [ConfigKey.SCREENSHOTS]: ScreenshotOption.ON, + [ConfigKey.SYNTHETICS_ARGS]: [], + [ConfigKey.JOURNEY_FILTERS_MATCH]: '', + [ConfigKey.JOURNEY_FILTERS_TAGS]: [], + [ConfigKey.IGNORE_HTTPS_ERRORS]: false, + [ConfigKey.IS_THROTTLING_ENABLED]: true, + [ConfigKey.DOWNLOAD_SPEED]: '5', + [ConfigKey.UPLOAD_SPEED]: '3', + [ConfigKey.LATENCY]: '20', + [ConfigKey.THROTTLING_CONFIG]: '5d/3u/20l', }; -const defaultContext: IBrowserAdvancedFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: BrowserAdvancedFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error( 'setFields was not initialized for Browser Advanced Fields, set it when you invoke the context' ); @@ -47,8 +47,8 @@ export const BrowserAdvancedFieldsContext = createContext(defaultContext); export const BrowserAdvancedFieldsContextProvider = ({ children, defaultValues = initialValues, -}: IBrowserAdvancedFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: BrowserAdvancedFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_provider.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_provider.tsx index e2ce88f84f7022..309784576818ff 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_provider.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/browser_provider.tsx @@ -6,7 +6,7 @@ */ import React, { ReactNode } from 'react'; -import { BrowserFields, IBrowserSimpleFields, IBrowserAdvancedFields } from '../types'; +import { BrowserFields, BrowserSimpleFields, BrowserAdvancedFields } from '../types'; import { BrowserSimpleFieldsContextProvider, BrowserAdvancedFieldsContextProvider, @@ -24,22 +24,22 @@ export const BrowserContextProvider = ({ defaultValues, children, }: BrowserContextProviderProps) => { - const simpleKeys = Object.keys(defaultBrowserSimpleFields) as Array; + const simpleKeys = Object.keys(defaultBrowserSimpleFields) as Array; const advancedKeys = Object.keys(defaultBrowserAdvancedFields) as Array< - keyof IBrowserAdvancedFields + keyof BrowserAdvancedFields >; - const formattedDefaultSimpleFields = formatDefaultValues( + const formattedDefaultSimpleFields = formatDefaultValues( simpleKeys, defaultValues || {} ); - const formattedDefaultAdvancedFields = formatDefaultValues( + const formattedDefaultAdvancedFields = formatDefaultValues( advancedKeys, defaultValues || {} ); - const simpleFields: IBrowserSimpleFields | undefined = defaultValues + const simpleFields: BrowserSimpleFields | undefined = defaultValues ? formattedDefaultSimpleFields : undefined; - const advancedFields: IBrowserAdvancedFields | undefined = defaultValues + const advancedFields: BrowserAdvancedFields | undefined = defaultValues ? formattedDefaultAdvancedFields : undefined; return ( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context.tsx index c29080cfcaf90a..a4a0c7bb16aa8d 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context.tsx @@ -6,32 +6,32 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { IHTTPSimpleFields, ConfigKeys, DataStream } from '../types'; +import { HTTPSimpleFields, ConfigKey, DataStream } from '../types'; import { defaultValues as commonDefaultValues } from '../common/default_values'; -interface IHTTPSimpleFieldsContext { - setFields: React.Dispatch>; - fields: IHTTPSimpleFields; - defaultValues: IHTTPSimpleFields; +interface HTTPSimpleFieldsContext { + setFields: React.Dispatch>; + fields: HTTPSimpleFields; + defaultValues: HTTPSimpleFields; } -interface IHTTPSimpleFieldsContextProvider { +interface HTTPSimpleFieldsContextProvider { children: React.ReactNode; - defaultValues?: IHTTPSimpleFields; + defaultValues?: HTTPSimpleFields; } -export const initialValues: IHTTPSimpleFields = { +export const initialValues: HTTPSimpleFields = { ...commonDefaultValues, - [ConfigKeys.METADATA]: { + [ConfigKey.METADATA]: { is_tls_enabled: false, }, - [ConfigKeys.URLS]: '', - [ConfigKeys.MAX_REDIRECTS]: '0', - [ConfigKeys.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.URLS]: '', + [ConfigKey.MAX_REDIRECTS]: '0', + [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, }; -const defaultContext: IHTTPSimpleFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: HTTPSimpleFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error( 'setFields was not initialized for HTTP Simple Fields, set it when you invoke the context' ); @@ -45,8 +45,8 @@ export const HTTPSimpleFieldsContext = createContext(defaultContext); export const HTTPSimpleFieldsContextProvider = ({ children, defaultValues = initialValues, -}: IHTTPSimpleFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: HTTPSimpleFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context_advanced.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context_advanced.tsx index 11796050a545b8..4d64bd3cf1ad49 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context_advanced.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_context_advanced.tsx @@ -6,45 +6,39 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { - IHTTPAdvancedFields, - ConfigKeys, - Mode, - ResponseBodyIndexPolicy, - HTTPMethod, -} from '../types'; +import { HTTPAdvancedFields, ConfigKey, Mode, ResponseBodyIndexPolicy, HTTPMethod } from '../types'; -interface IHTTPAdvancedFieldsContext { - setFields: React.Dispatch>; - fields: IHTTPAdvancedFields; - defaultValues: IHTTPAdvancedFields; +interface HTTPAdvancedFieldsContext { + setFields: React.Dispatch>; + fields: HTTPAdvancedFields; + defaultValues: HTTPAdvancedFields; } -interface IHTTPAdvancedFieldsContextProvider { +interface HTTPAdvancedFieldsContextProvider { children: React.ReactNode; - defaultValues?: IHTTPAdvancedFields; + defaultValues?: HTTPAdvancedFields; } -export const initialValues: IHTTPAdvancedFields = { - [ConfigKeys.PASSWORD]: '', - [ConfigKeys.PROXY_URL]: '', - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: [], - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: [], - [ConfigKeys.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicy.ON_ERROR, - [ConfigKeys.RESPONSE_HEADERS_CHECK]: {}, - [ConfigKeys.RESPONSE_HEADERS_INDEX]: true, - [ConfigKeys.RESPONSE_STATUS_CHECK]: [], - [ConfigKeys.REQUEST_BODY_CHECK]: { +export const initialValues: HTTPAdvancedFields = { + [ConfigKey.PASSWORD]: '', + [ConfigKey.PROXY_URL]: '', + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], + [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicy.ON_ERROR, + [ConfigKey.RESPONSE_HEADERS_CHECK]: {}, + [ConfigKey.RESPONSE_HEADERS_INDEX]: true, + [ConfigKey.RESPONSE_STATUS_CHECK]: [], + [ConfigKey.REQUEST_BODY_CHECK]: { value: '', type: Mode.PLAINTEXT, }, - [ConfigKeys.REQUEST_HEADERS_CHECK]: {}, - [ConfigKeys.REQUEST_METHOD_CHECK]: HTTPMethod.GET, - [ConfigKeys.USERNAME]: '', + [ConfigKey.REQUEST_HEADERS_CHECK]: {}, + [ConfigKey.REQUEST_METHOD_CHECK]: HTTPMethod.GET, + [ConfigKey.USERNAME]: '', }; -export const defaultContext: IHTTPAdvancedFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +export const defaultContext: HTTPAdvancedFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error('setFields was not initialized, set it when you invoke the context'); }, fields: initialValues, @@ -56,8 +50,8 @@ export const HTTPAdvancedFieldsContext = createContext(defaultContext); export const HTTPAdvancedFieldsContextProvider = ({ children, defaultValues = initialValues, -}: IHTTPAdvancedFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: HTTPAdvancedFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_provider.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_provider.tsx index ea577f33369360..9141594dd5ccfa 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_provider.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/http_provider.tsx @@ -6,7 +6,7 @@ */ import React, { ReactNode } from 'react'; -import { HTTPFields, IHTTPSimpleFields, IHTTPAdvancedFields } from '../types'; +import { HTTPFields, HTTPSimpleFields, HTTPAdvancedFields } from '../types'; import { HTTPSimpleFieldsContextProvider, HTTPAdvancedFieldsContextProvider, @@ -21,18 +21,18 @@ interface HTTPContextProviderProps { } export const HTTPContextProvider = ({ defaultValues, children }: HTTPContextProviderProps) => { - const simpleKeys = Object.keys(defaultHTTPSimpleFields) as Array; - const advancedKeys = Object.keys(defaultHTTPAdvancedFields) as Array; - const formattedDefaultHTTPSimpleFields = formatDefaultValues( + const simpleKeys = Object.keys(defaultHTTPSimpleFields) as Array; + const advancedKeys = Object.keys(defaultHTTPAdvancedFields) as Array; + const formattedDefaultHTTPSimpleFields = formatDefaultValues( simpleKeys, defaultValues || {} ); - const formattedDefaultHTTPAdvancedFields = formatDefaultValues( + const formattedDefaultHTTPAdvancedFields = formatDefaultValues( advancedKeys, defaultValues || {} ); const httpAdvancedFields = defaultValues ? formattedDefaultHTTPAdvancedFields : undefined; - const httpSimpleFields: IHTTPSimpleFields | undefined = defaultValues + const httpSimpleFields: HTTPSimpleFields | undefined = defaultValues ? formattedDefaultHTTPSimpleFields : undefined; return ( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/icmp_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/icmp_context.tsx index eb7227ebceb07b..f0547e621afc4c 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/icmp_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/icmp_context.tsx @@ -6,29 +6,29 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { IICMPSimpleFields, ConfigKeys, DataStream } from '../types'; +import { ICMPSimpleFields, ConfigKey, DataStream } from '../types'; import { defaultValues as commonDefaultValues } from '../common/default_values'; -interface IICMPSimpleFieldsContext { - setFields: React.Dispatch>; - fields: IICMPSimpleFields; - defaultValues: IICMPSimpleFields; +interface ICMPSimpleFieldsContext { + setFields: React.Dispatch>; + fields: ICMPSimpleFields; + defaultValues: ICMPSimpleFields; } -interface IICMPSimpleFieldsContextProvider { +interface ICMPSimpleFieldsContextProvider { children: React.ReactNode; - defaultValues?: IICMPSimpleFields; + defaultValues?: ICMPSimpleFields; } -export const initialValues: IICMPSimpleFields = { +export const initialValues: ICMPSimpleFields = { ...commonDefaultValues, - [ConfigKeys.HOSTS]: '', - [ConfigKeys.MONITOR_TYPE]: DataStream.ICMP, - [ConfigKeys.WAIT]: '1', + [ConfigKey.HOSTS]: '', + [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.WAIT]: '1', }; -const defaultContext: IICMPSimpleFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: ICMPSimpleFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error( 'setFields was not initialized for ICMP Simple Fields, set it when you invoke the context' ); @@ -42,8 +42,8 @@ export const ICMPSimpleFieldsContext = createContext(defaultContext); export const ICMPSimpleFieldsContextProvider = ({ children, defaultValues = initialValues, -}: IICMPSimpleFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: ICMPSimpleFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/synthetics_context_providers.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/synthetics_context_providers.tsx index 0d730c5f96e970..f096da7dda5734 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/synthetics_context_providers.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/synthetics_context_providers.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HTTPFields, TCPFields, ICMPFields, BrowserFields, ITLSFields, DataStream } from '../types'; +import { HTTPFields, TCPFields, ICMPFields, BrowserFields, TLSFields, DataStream } from '../types'; import { PolicyConfigContextProvider, TCPContextProvider, @@ -22,7 +22,7 @@ interface Props { tcpDefaultValues?: TCPFields; icmpDefaultValues?: ICMPFields; browserDefaultValues?: BrowserFields; - tlsDefaultValues?: ITLSFields; + tlsDefaultValues?: TLSFields; policyDefaultValues?: { defaultMonitorType: DataStream; defaultIsTLSEnabled: boolean; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context.tsx index c084ea757035fa..38a15c3e394531 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context.tsx @@ -6,31 +6,31 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { ITCPSimpleFields, ConfigKeys, DataStream } from '../types'; +import { TCPSimpleFields, ConfigKey, DataStream } from '../types'; import { defaultValues as commonDefaultValues } from '../common/default_values'; -interface ITCPSimpleFieldsContext { - setFields: React.Dispatch>; - fields: ITCPSimpleFields; - defaultValues: ITCPSimpleFields; +interface TCPSimpleFieldsContext { + setFields: React.Dispatch>; + fields: TCPSimpleFields; + defaultValues: TCPSimpleFields; } -interface ITCPSimpleFieldsContextProvider { +interface TCPSimpleFieldsContextProvider { children: React.ReactNode; - defaultValues?: ITCPSimpleFields; + defaultValues?: TCPSimpleFields; } -export const initialValues: ITCPSimpleFields = { +export const initialValues: TCPSimpleFields = { ...commonDefaultValues, - [ConfigKeys.METADATA]: { + [ConfigKey.METADATA]: { is_tls_enabled: false, }, - [ConfigKeys.HOSTS]: '', - [ConfigKeys.MONITOR_TYPE]: DataStream.TCP, + [ConfigKey.HOSTS]: '', + [ConfigKey.MONITOR_TYPE]: DataStream.TCP, }; -const defaultContext: ITCPSimpleFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: TCPSimpleFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error( 'setFields was not initialized for TCP Simple Fields, set it when you invoke the context' ); @@ -44,8 +44,8 @@ export const TCPSimpleFieldsContext = createContext(defaultContext); export const TCPSimpleFieldsContextProvider = ({ children, defaultValues = initialValues, -}: ITCPSimpleFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: TCPSimpleFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context_advanced.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context_advanced.tsx index ef821b7e39dcaf..7fe29f76488412 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context_advanced.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_context_advanced.tsx @@ -6,28 +6,28 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { ITCPAdvancedFields, ConfigKeys } from '../types'; +import { TCPAdvancedFields, ConfigKey } from '../types'; -interface ITCPAdvancedFieldsContext { - setFields: React.Dispatch>; - fields: ITCPAdvancedFields; - defaultValues: ITCPAdvancedFields; +interface TCPAdvancedFieldsContext { + setFields: React.Dispatch>; + fields: TCPAdvancedFields; + defaultValues: TCPAdvancedFields; } -interface ITCPAdvancedFieldsContextProvider { +interface TCPAdvancedFieldsContextProvider { children: React.ReactNode; - defaultValues?: ITCPAdvancedFields; + defaultValues?: TCPAdvancedFields; } -export const initialValues: ITCPAdvancedFields = { - [ConfigKeys.PROXY_URL]: '', - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: false, - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: '', - [ConfigKeys.REQUEST_SEND_CHECK]: '', +export const initialValues: TCPAdvancedFields = { + [ConfigKey.PROXY_URL]: '', + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: false, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: '', + [ConfigKey.REQUEST_SEND_CHECK]: '', }; -const defaultContext: ITCPAdvancedFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: TCPAdvancedFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error('setFields was not initialized, set it when you invoke the context'); }, fields: initialValues, // mutable @@ -39,8 +39,8 @@ export const TCPAdvancedFieldsContext = createContext(defaultContext); export const TCPAdvancedFieldsContextProvider = ({ children, defaultValues = initialValues, -}: ITCPAdvancedFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: TCPAdvancedFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_provider.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_provider.tsx index b62e87a566b97c..76877319c0c485 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_provider.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tcp_provider.tsx @@ -6,7 +6,7 @@ */ import React, { ReactNode } from 'react'; -import { TCPFields, ITCPSimpleFields, ITCPAdvancedFields } from '../types'; +import { TCPFields, TCPSimpleFields, TCPAdvancedFields } from '../types'; import { TCPSimpleFieldsContextProvider, TCPAdvancedFieldsContextProvider, @@ -21,20 +21,20 @@ interface TCPContextProviderProps { } export const TCPContextProvider = ({ defaultValues, children }: TCPContextProviderProps) => { - const simpleKeys = Object.keys(defaultTCPSimpleFields) as Array; - const advancedKeys = Object.keys(defaultTCPAdvancedFields) as Array; - const formattedDefaultSimpleFields = formatDefaultValues( + const simpleKeys = Object.keys(defaultTCPSimpleFields) as Array; + const advancedKeys = Object.keys(defaultTCPAdvancedFields) as Array; + const formattedDefaultSimpleFields = formatDefaultValues( simpleKeys, defaultValues || {} ); - const formattedDefaultAdvancedFields = formatDefaultValues( + const formattedDefaultAdvancedFields = formatDefaultValues( advancedKeys, defaultValues || {} ); - const simpleFields: ITCPSimpleFields | undefined = defaultValues + const simpleFields: TCPSimpleFields | undefined = defaultValues ? formattedDefaultSimpleFields : undefined; - const advancedFields: ITCPAdvancedFields | undefined = defaultValues + const advancedFields: TCPAdvancedFields | undefined = defaultValues ? formattedDefaultAdvancedFields : undefined; return ( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tls_fields_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tls_fields_context.tsx index e754d0ca03257f..a76655a235c4fc 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/tls_fields_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/tls_fields_context.tsx @@ -6,24 +6,24 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { ITLSFields } from '../types'; +import { TLSFields } from '../types'; import { defaultValues as tlsDefaultValues } from '../tls/default_values'; -interface ITLSFieldsContext { - setFields: React.Dispatch>; - fields: ITLSFields; - defaultValues: ITLSFields; +interface TLSFieldsContext { + setFields: React.Dispatch>; + fields: TLSFields; + defaultValues: TLSFields; } -interface ITLSFieldsContextProvider { +interface TLSFieldsContextProvider { children: React.ReactNode; - defaultValues?: ITLSFields; + defaultValues?: TLSFields; } -export const initialValues: ITLSFields = tlsDefaultValues; +export const initialValues: TLSFields = tlsDefaultValues; -const defaultContext: ITLSFieldsContext = { - setFields: (_fields: React.SetStateAction) => { +const defaultContext: TLSFieldsContext = { + setFields: (_fields: React.SetStateAction) => { throw new Error('setFields was not initialized, set it when you invoke the context'); }, fields: initialValues, // mutable @@ -35,8 +35,8 @@ export const TLSFieldsContext = createContext(defaultContext); export const TLSFieldsContextProvider = ({ children, defaultValues = initialValues, -}: ITLSFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); +}: TLSFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); const value = useMemo(() => { return { fields, setFields, defaultValues }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx index c70424f7910f4d..2aed5db789f44c 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx @@ -18,7 +18,7 @@ import { TLSFieldsContextProvider, } from './contexts'; import { CustomFields } from './custom_fields'; -import { ConfigKeys, DataStream, ScheduleUnit } from './types'; +import { ConfigKey, DataStream, ScheduleUnit } from './types'; import { validate as centralValidation } from './validation'; import { defaultConfig } from './synthetics_policy_create_extension'; @@ -85,20 +85,20 @@ describe('', () => { const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; expect(monitorType).toBeInTheDocument(); expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKeys.URLS]); + expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); // expect(tags).toBeInTheDocument(); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKeys.MAX_REDIRECTS]}`); + expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Host')).not.toBeInTheDocument(); @@ -145,11 +145,11 @@ describe('', () => { expect(verificationMode).toBeInTheDocument(); await waitFor(() => { - expect(ca.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]); - expect(clientKey.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_KEY]); - expect(clientKeyPassphrase.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_KEY_PASSPHRASE]); - expect(clientCertificate.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_CERTIFICATE]); - expect(verificationMode.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_VERIFICATION_MODE]); + expect(ca.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]); + expect(clientKey.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_KEY]); + expect(clientKeyPassphrase.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_KEY_PASSPHRASE]); + expect(clientCertificate.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_CERTIFICATE]); + expect(verificationMode.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]); }); }); @@ -186,14 +186,14 @@ describe('', () => { ); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; expect(monitorType).toBeInTheDocument(); - expect(monitorType.value).toEqual(defaultHTTPConfig[ConfigKeys.MONITOR_TYPE]); + expect(monitorType.value).toEqual(defaultHTTPConfig[ConfigKey.MONITOR_TYPE]); fireEvent.change(monitorType, { target: { value: DataStream.TCP } }); // expect tcp fields to be in the DOM const host = getByLabelText('Host:Port') as HTMLInputElement; expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultTCPConfig[ConfigKeys.HOSTS]); + expect(host.value).toEqual(defaultTCPConfig[ConfigKey.HOSTS]); // expect HTTP fields not to be in the DOM expect(queryByLabelText('URL')).not.toBeInTheDocument(); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx index e136ccf58a3406..50f8d93ca8f4ac 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx @@ -19,7 +19,7 @@ import { EuiCallOut, EuiLink, } from '@elastic/eui'; -import { ConfigKeys, DataStream, Validation } from './types'; +import { ConfigKey, DataStream, Validation } from './types'; import { usePolicyConfigContext } from './contexts'; import { TLSFields } from './tls_fields'; import { HTTPSimpleFields } from './http/simple_fields'; @@ -109,8 +109,8 @@ export const CustomFields = memo(({ validate, dataStreams = [], children /> } isInvalid={ - !!validate[ConfigKeys.MONITOR_TYPE]?.({ - [ConfigKeys.MONITOR_TYPE]: monitorType, + !!validate[ConfigKey.MONITOR_TYPE]?.({ + [ConfigKey.MONITOR_TYPE]: monitorType as DataStream, }) } error={ diff --git a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_policy.ts b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_policy.ts index 477999abdc05ad..e28c5c5f9d5972 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_policy.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_policy.ts @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import { PolicyConfig, DataStream, - ConfigKeys, + ConfigKey, HTTPFields, TCPFields, ICMPFields, @@ -84,34 +84,34 @@ export const usePolicy = (fleetPolicyName: string = '') => { ...httpSimpleFields, ...httpAdvancedFields, ...tlsFields, - [ConfigKeys.METADATA]: { - ...httpSimpleFields[ConfigKeys.METADATA], + [ConfigKey.METADATA]: { + ...httpSimpleFields[ConfigKey.METADATA], ...metadata, }, - [ConfigKeys.NAME]: fleetPolicyName || monitorName, + [ConfigKey.NAME]: fleetPolicyName || monitorName, } as HTTPFields, [DataStream.TCP]: { ...tcpSimpleFields, ...tcpAdvancedFields, ...tlsFields, - [ConfigKeys.METADATA]: { - ...tcpSimpleFields[ConfigKeys.METADATA], + [ConfigKey.METADATA]: { + ...tcpSimpleFields[ConfigKey.METADATA], ...metadata, }, - [ConfigKeys.NAME]: fleetPolicyName || monitorName, + [ConfigKey.NAME]: fleetPolicyName || monitorName, } as TCPFields, [DataStream.ICMP]: { ...icmpSimpleFields, - [ConfigKeys.NAME]: fleetPolicyName || monitorName, + [ConfigKey.NAME]: fleetPolicyName || monitorName, } as ICMPFields, [DataStream.BROWSER]: { ...browserSimpleFields, ...browserAdvancedFields, - [ConfigKeys.METADATA]: { - ...browserSimpleFields[ConfigKeys.METADATA], + [ConfigKey.METADATA]: { + ...browserSimpleFields[ConfigKey.METADATA], ...metadata, }, - [ConfigKeys.NAME]: fleetPolicyName || monitorName, + [ConfigKey.NAME]: fleetPolicyName || monitorName, } as BrowserFields, }), [ diff --git a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx index 40cfdfeba49a5a..c8a96c8eede836 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx @@ -9,14 +9,14 @@ import { useUpdatePolicy } from './use_update_policy'; import { NewPackagePolicy } from '../../../../../fleet/public'; import { validate } from '../validation'; import { - ConfigKeys, + ConfigKey, DataStream, TLSVersion, - ICommonFields, + CommonFields, ScheduleUnit, ICMPFields, TCPFields, - ITLSFields, + TLSFields, HTTPFields, BrowserFields, } from '../types'; @@ -336,21 +336,21 @@ describe('useBarChartsHooks', () => { }, }; - const defaultCommonFields: Partial = { - [ConfigKeys.APM_SERVICE_NAME]: 'APM Service name', - [ConfigKeys.TAGS]: ['some', 'tags'], - [ConfigKeys.SCHEDULE]: { + const defaultCommonFields: Partial = { + [ConfigKey.APM_SERVICE_NAME]: 'APM Service name', + [ConfigKey.TAGS]: ['some', 'tags'], + [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES, }, - [ConfigKeys.TIMEOUT]: '17', + [ConfigKey.TIMEOUT]: '17', }; - const defaultTLSFields: Partial = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: 'ca', - [ConfigKeys.TLS_CERTIFICATE]: 'cert', - [ConfigKeys.TLS_KEY]: 'key', - [ConfigKeys.TLS_KEY_PASSPHRASE]: 'password', + const defaultTLSFields: Partial = { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'ca', + [ConfigKey.TLS_CERTIFICATE]: 'cert', + [ConfigKey.TLS_KEY]: 'key', + [ConfigKey.TLS_KEY_PASSPHRASE]: 'password', }; it('handles http data stream', async () => { @@ -373,8 +373,8 @@ describe('useBarChartsHooks', () => { ...defaultConfig[DataStream.HTTP], ...defaultCommonFields, ...defaultTLSFields, - [ConfigKeys.URLS]: 'url', - [ConfigKeys.PROXY_URL]: 'proxyUrl', + [ConfigKey.URLS]: 'url', + [ConfigKey.PROXY_URL]: 'proxyUrl', }; // expect only http to be enabled @@ -391,28 +391,26 @@ describe('useBarChartsHooks', () => { await waitFor(() => { const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - expect(vars?.[ConfigKeys.MONITOR_TYPE].value).toEqual(config[ConfigKeys.MONITOR_TYPE]); - expect(vars?.[ConfigKeys.URLS].value).toEqual(config[ConfigKeys.URLS]); - expect(vars?.[ConfigKeys.SCHEDULE].value).toEqual( + expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); + expect(vars?.[ConfigKey.URLS].value).toEqual(config[ConfigKey.URLS]); + expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( JSON.stringify( - `@every ${config[ConfigKeys.SCHEDULE].number}${config[ConfigKeys.SCHEDULE].unit}` + `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` ) ); - expect(vars?.[ConfigKeys.PROXY_URL].value).toEqual(config[ConfigKeys.PROXY_URL]); - expect(vars?.[ConfigKeys.APM_SERVICE_NAME].value).toEqual( - config[ConfigKeys.APM_SERVICE_NAME] + expect(vars?.[ConfigKey.PROXY_URL].value).toEqual(config[ConfigKey.PROXY_URL]); + expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); + expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual(null); + expect(vars?.[ConfigKey.REQUEST_HEADERS_CHECK].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_HEADERS_CHECK].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_BODY_INDEX].value).toEqual( + config[ConfigKey.RESPONSE_BODY_INDEX] ); - expect(vars?.[ConfigKeys.TIMEOUT].value).toEqual(`${config[ConfigKeys.TIMEOUT]}s`); - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_STATUS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKeys.REQUEST_HEADERS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_HEADERS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_BODY_INDEX].value).toEqual( - config[ConfigKeys.RESPONSE_BODY_INDEX] - ); - expect(vars?.[ConfigKeys.RESPONSE_HEADERS_INDEX].value).toEqual( - config[ConfigKeys.RESPONSE_HEADERS_INDEX] + expect(vars?.[ConfigKey.RESPONSE_HEADERS_INDEX].value).toEqual( + config[ConfigKey.RESPONSE_HEADERS_INDEX] ); }); }); @@ -435,11 +433,11 @@ describe('useBarChartsHooks', () => { ...initialProps, config: { ...defaultConfig[DataStream.HTTP], - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: ['test'], - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: ['test'], - [ConfigKeys.RESPONSE_STATUS_CHECK]: ['test'], - [ConfigKeys.TAGS]: ['test'], - [ConfigKeys.TLS_VERSION]: [TLSVersion.ONE_ONE], + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: ['test'], + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: ['test'], + [ConfigKey.RESPONSE_STATUS_CHECK]: ['test'], + [ConfigKey.TAGS]: ['test'], + [ConfigKey.TLS_VERSION]: [TLSVersion.ONE_ONE], }, }); @@ -452,33 +450,33 @@ describe('useBarChartsHooks', () => { const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual('["test"]'); - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual('["test"]'); - expect(vars?.[ConfigKeys.RESPONSE_STATUS_CHECK].value).toEqual('["test"]'); - expect(vars?.[ConfigKeys.TAGS].value).toEqual('["test"]'); - expect(vars?.[ConfigKeys.TLS_VERSION].value).toEqual('["TLSv1.1"]'); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual('["test"]'); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual('["test"]'); + expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual('["test"]'); + expect(vars?.[ConfigKey.TAGS].value).toEqual('["test"]'); + expect(vars?.[ConfigKey.TLS_VERSION].value).toEqual('["TLSv1.1"]'); }); rerender({ ...initialProps, config: { ...defaultConfig[DataStream.HTTP], - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: [], - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: [], - [ConfigKeys.RESPONSE_STATUS_CHECK]: [], - [ConfigKeys.TAGS]: [], - [ConfigKeys.TLS_VERSION]: [], + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], + [ConfigKey.RESPONSE_STATUS_CHECK]: [], + [ConfigKey.TAGS]: [], + [ConfigKey.TLS_VERSION]: [], }, }); await waitFor(() => { const vars = result.current.updatedPolicy.inputs[0]?.streams[0]?.vars; - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); - expect(vars?.[ConfigKeys.RESPONSE_STATUS_CHECK].value).toEqual(null); - expect(vars?.[ConfigKeys.TAGS].value).toEqual(null); - expect(vars?.[ConfigKeys.TLS_VERSION].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE].value).toEqual(null); + expect(vars?.[ConfigKey.RESPONSE_STATUS_CHECK].value).toEqual(null); + expect(vars?.[ConfigKey.TAGS].value).toEqual(null); + expect(vars?.[ConfigKey.TLS_VERSION].value).toEqual(null); }); }); @@ -506,11 +504,11 @@ describe('useBarChartsHooks', () => { ...defaultConfig[DataStream.TCP], ...defaultCommonFields, ...defaultTLSFields, - [ConfigKeys.HOSTS]: 'sampleHost', - [ConfigKeys.PROXY_URL]: 'proxyUrl', - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: true, - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: 'response', - [ConfigKeys.REQUEST_SEND_CHECK]: 'request', + [ConfigKey.HOSTS]: 'sampleHost', + [ConfigKey.PROXY_URL]: 'proxyUrl', + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: true, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: 'response', + [ConfigKey.REQUEST_SEND_CHECK]: 'request', }; rerender({ @@ -526,26 +524,24 @@ describe('useBarChartsHooks', () => { updatedPolicy: result.current.updatedPolicy, }); - expect(vars?.[ConfigKeys.MONITOR_TYPE].value).toEqual(config[ConfigKeys.MONITOR_TYPE]); - expect(vars?.[ConfigKeys.HOSTS].value).toEqual(config[ConfigKeys.HOSTS]); - expect(vars?.[ConfigKeys.SCHEDULE].value).toEqual( + expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); + expect(vars?.[ConfigKey.HOSTS].value).toEqual(config[ConfigKey.HOSTS]); + expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( JSON.stringify( - `@every ${config[ConfigKeys.SCHEDULE].number}${config[ConfigKeys.SCHEDULE].unit}` + `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` ) ); - expect(vars?.[ConfigKeys.PROXY_URL].value).toEqual(config[ConfigKeys.PROXY_URL]); - expect(vars?.[ConfigKeys.APM_SERVICE_NAME].value).toEqual( - config[ConfigKeys.APM_SERVICE_NAME] - ); - expect(vars?.[ConfigKeys.TIMEOUT].value).toEqual(`${config[ConfigKeys.TIMEOUT]}s`); - expect(vars?.[ConfigKeys.PROXY_USE_LOCAL_RESOLVER].value).toEqual( - config[ConfigKeys.PROXY_USE_LOCAL_RESOLVER] + expect(vars?.[ConfigKey.PROXY_URL].value).toEqual(config[ConfigKey.PROXY_URL]); + expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); + expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); + expect(vars?.[ConfigKey.PROXY_USE_LOCAL_RESOLVER].value).toEqual( + config[ConfigKey.PROXY_USE_LOCAL_RESOLVER] ); - expect(vars?.[ConfigKeys.RESPONSE_RECEIVE_CHECK].value).toEqual( - config[ConfigKeys.RESPONSE_RECEIVE_CHECK] + expect(vars?.[ConfigKey.RESPONSE_RECEIVE_CHECK].value).toEqual( + config[ConfigKey.RESPONSE_RECEIVE_CHECK] ); - expect(vars?.[ConfigKeys.REQUEST_SEND_CHECK].value).toEqual( - config[ConfigKeys.REQUEST_SEND_CHECK] + expect(vars?.[ConfigKey.REQUEST_SEND_CHECK].value).toEqual( + config[ConfigKey.REQUEST_SEND_CHECK] ); }); }); @@ -566,8 +562,8 @@ describe('useBarChartsHooks', () => { const config: ICMPFields = { ...defaultConfig[DataStream.ICMP], ...defaultCommonFields, - [ConfigKeys.WAIT]: '2', - [ConfigKeys.HOSTS]: 'sampleHost', + [ConfigKey.WAIT]: '2', + [ConfigKey.HOSTS]: 'sampleHost', }; // expect only icmp to be enabled @@ -585,18 +581,16 @@ describe('useBarChartsHooks', () => { await waitFor(() => { const vars = result.current.updatedPolicy.inputs[2]?.streams[0]?.vars; - expect(vars?.[ConfigKeys.MONITOR_TYPE].value).toEqual(config[ConfigKeys.MONITOR_TYPE]); - expect(vars?.[ConfigKeys.HOSTS].value).toEqual(config[ConfigKeys.HOSTS]); - expect(vars?.[ConfigKeys.SCHEDULE].value).toEqual( + expect(vars?.[ConfigKey.MONITOR_TYPE].value).toEqual(config[ConfigKey.MONITOR_TYPE]); + expect(vars?.[ConfigKey.HOSTS].value).toEqual(config[ConfigKey.HOSTS]); + expect(vars?.[ConfigKey.SCHEDULE].value).toEqual( JSON.stringify( - `@every ${config[ConfigKeys.SCHEDULE].number}${config[ConfigKeys.SCHEDULE].unit}` + `@every ${config[ConfigKey.SCHEDULE].number}${config[ConfigKey.SCHEDULE].unit}` ) ); - expect(vars?.[ConfigKeys.APM_SERVICE_NAME].value).toEqual( - config[ConfigKeys.APM_SERVICE_NAME] - ); - expect(vars?.[ConfigKeys.TIMEOUT].value).toEqual(`${config[ConfigKeys.TIMEOUT]}s`); - expect(vars?.[ConfigKeys.WAIT].value).toEqual(`${config[ConfigKeys.WAIT]}s`); + expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); + expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); + expect(vars?.[ConfigKey.WAIT].value).toEqual(`${config[ConfigKey.WAIT]}s`); expect(onChange).toBeCalledWith({ isValid: false, @@ -629,16 +623,16 @@ describe('useBarChartsHooks', () => { const config: BrowserFields = { ...defaultConfig[DataStream.BROWSER], ...defaultCommonFields, - [ConfigKeys.SOURCE_INLINE]: 'inlineScript', - [ConfigKeys.SOURCE_ZIP_URL]: 'zipFolder', - [ConfigKeys.SOURCE_ZIP_FOLDER]: 'zipFolder', - [ConfigKeys.SOURCE_ZIP_USERNAME]: 'username', - [ConfigKeys.SOURCE_ZIP_PASSWORD]: 'password', - [ConfigKeys.SCREENSHOTS]: 'off', - [ConfigKeys.SYNTHETICS_ARGS]: ['args'], - [ConfigKeys.DOWNLOAD_SPEED]: '13', - [ConfigKeys.UPLOAD_SPEED]: '3', - [ConfigKeys.LATENCY]: '7', + [ConfigKey.SOURCE_INLINE]: 'inlineScript', + [ConfigKey.SOURCE_ZIP_URL]: 'zipFolder', + [ConfigKey.SOURCE_ZIP_FOLDER]: 'zipFolder', + [ConfigKey.SOURCE_ZIP_USERNAME]: 'username', + [ConfigKey.SOURCE_ZIP_PASSWORD]: 'password', + [ConfigKey.SCREENSHOTS]: 'off', + [ConfigKey.SYNTHETICS_ARGS]: ['args'], + [ConfigKey.DOWNLOAD_SPEED]: '13', + [ConfigKey.UPLOAD_SPEED]: '3', + [ConfigKey.LATENCY]: '7', }; rerender({ @@ -649,30 +643,28 @@ describe('useBarChartsHooks', () => { await waitFor(() => { const vars = result.current.updatedPolicy.inputs[3]?.streams[0]?.vars; - expect(vars?.[ConfigKeys.SOURCE_ZIP_FOLDER].value).toEqual( - config[ConfigKeys.SOURCE_ZIP_FOLDER] - ); - expect(vars?.[ConfigKeys.SOURCE_ZIP_PASSWORD].value).toEqual( - config[ConfigKeys.SOURCE_ZIP_PASSWORD] + expect(vars?.[ConfigKey.SOURCE_ZIP_FOLDER].value).toEqual( + config[ConfigKey.SOURCE_ZIP_FOLDER] ); - expect(vars?.[ConfigKeys.SOURCE_ZIP_URL].value).toEqual(config[ConfigKeys.SOURCE_ZIP_URL]); - expect(vars?.[ConfigKeys.SOURCE_INLINE].value).toEqual( - JSON.stringify(config[ConfigKeys.SOURCE_INLINE]) + expect(vars?.[ConfigKey.SOURCE_ZIP_PASSWORD].value).toEqual( + config[ConfigKey.SOURCE_ZIP_PASSWORD] ); - expect(vars?.[ConfigKeys.SOURCE_ZIP_PASSWORD].value).toEqual( - config[ConfigKeys.SOURCE_ZIP_PASSWORD] + expect(vars?.[ConfigKey.SOURCE_ZIP_URL].value).toEqual(config[ConfigKey.SOURCE_ZIP_URL]); + expect(vars?.[ConfigKey.SOURCE_INLINE].value).toEqual( + JSON.stringify(config[ConfigKey.SOURCE_INLINE]) ); - expect(vars?.[ConfigKeys.SCREENSHOTS].value).toEqual(config[ConfigKeys.SCREENSHOTS]); - expect(vars?.[ConfigKeys.SYNTHETICS_ARGS].value).toEqual( - JSON.stringify(config[ConfigKeys.SYNTHETICS_ARGS]) + expect(vars?.[ConfigKey.SOURCE_ZIP_PASSWORD].value).toEqual( + config[ConfigKey.SOURCE_ZIP_PASSWORD] ); - expect(vars?.[ConfigKeys.APM_SERVICE_NAME].value).toEqual( - config[ConfigKeys.APM_SERVICE_NAME] + expect(vars?.[ConfigKey.SCREENSHOTS].value).toEqual(config[ConfigKey.SCREENSHOTS]); + expect(vars?.[ConfigKey.SYNTHETICS_ARGS].value).toEqual( + JSON.stringify(config[ConfigKey.SYNTHETICS_ARGS]) ); - expect(vars?.[ConfigKeys.TIMEOUT].value).toEqual(`${config[ConfigKeys.TIMEOUT]}s`); - expect(vars?.[ConfigKeys.THROTTLING_CONFIG].value).toEqual( - `${config[ConfigKeys.DOWNLOAD_SPEED]}d/${config[ConfigKeys.UPLOAD_SPEED]}u/${ - config[ConfigKeys.LATENCY] + expect(vars?.[ConfigKey.APM_SERVICE_NAME].value).toEqual(config[ConfigKey.APM_SERVICE_NAME]); + expect(vars?.[ConfigKey.TIMEOUT].value).toEqual(`${config[ConfigKey.TIMEOUT]}s`); + expect(vars?.[ConfigKey.THROTTLING_CONFIG].value).toEqual( + `${config[ConfigKey.DOWNLOAD_SPEED]}d/${config[ConfigKey.UPLOAD_SPEED]}u/${ + config[ConfigKey.LATENCY] }l` ); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts index 17ded6385da4fa..8c312c8c9abfa9 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts @@ -6,13 +6,13 @@ */ import { useEffect, useRef, useState } from 'react'; import { NewPackagePolicy } from '../../../../../fleet/public'; -import { ConfigKeys, DataStream, Validation, ICustomFields } from '../types'; +import { ConfigKey, DataStream, Validation, MonitorFields } from '../types'; import { formatters } from '../helpers/formatters'; interface Props { monitorType: DataStream; - defaultConfig: Partial; - config: Partial; + defaultConfig: Partial; + config: Partial; newPolicy: NewPackagePolicy; onChange: (opts: { /** is current form state is valid */ @@ -33,11 +33,11 @@ export const useUpdatePolicy = ({ }: Props) => { const [updatedPolicy, setUpdatedPolicy] = useState(newPolicy); // Update the integration policy with our custom fields - const currentConfig = useRef>(defaultConfig); + const currentConfig = useRef>(defaultConfig); useEffect(() => { - const configKeys = Object.keys(config) as ConfigKeys[]; - const validationKeys = Object.keys(validate[monitorType]) as ConfigKeys[]; + const configKeys = Object.keys(config) as ConfigKey[]; + const validationKeys = Object.keys(validate[monitorType]) as ConfigKey[]; const configDidUpdate = configKeys.some((key) => config[key] !== currentConfig.current[key]); const isValid = !!newPolicy.name && !validationKeys.find((key) => validate[monitorType]?.[key]?.(config)); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx index 0b434b6677353b..8467abbab6c47f 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx @@ -9,7 +9,13 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; import { HTTPAdvancedFields } from './advanced_fields'; -import { ConfigKeys, DataStream, HTTPMethod, IHTTPAdvancedFields, Validation } from '../types'; +import { + ConfigKey, + DataStream, + HTTPMethod, + HTTPAdvancedFields as HTTPAdvancedFieldsType, + Validation, +} from '../types'; import { HTTPAdvancedFieldsContextProvider, defaultHTTPAdvancedFields as defaultConfig, @@ -44,7 +50,7 @@ describe('', () => { defaultValues, validate = defaultValidation, }: { - defaultValues?: IHTTPAdvancedFields; + defaultValues?: HTTPAdvancedFieldsType; validate?: Validation; }) => { return ( @@ -73,25 +79,25 @@ describe('', () => { const username = getByLabelText('Username') as HTMLInputElement; const password = getByLabelText('Password') as HTMLInputElement; expect(requestMethod).toBeInTheDocument(); - expect(requestMethod.value).toEqual(defaultConfig[ConfigKeys.REQUEST_METHOD_CHECK]); + expect(requestMethod.value).toEqual(defaultConfig[ConfigKey.REQUEST_METHOD_CHECK]); expect(requestHeaders).toBeInTheDocument(); expect(requestBody).toBeInTheDocument(); expect(indexResponseBody).toBeInTheDocument(); expect(indexResponseBody.checked).toBe(true); expect(indexResponseBodySelect).toBeInTheDocument(); - expect(indexResponseBodySelect.value).toEqual(defaultConfig[ConfigKeys.RESPONSE_BODY_INDEX]); + expect(indexResponseBodySelect.value).toEqual(defaultConfig[ConfigKey.RESPONSE_BODY_INDEX]); expect(indexResponseHeaders).toBeInTheDocument(); expect(indexResponseHeaders.checked).toBe(true); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultConfig[ConfigKey.PROXY_URL]); expect(responseStatusEquals).toBeInTheDocument(); expect(responseBodyContains).toBeInTheDocument(); expect(responseBodyDoesNotContain).toBeInTheDocument(); expect(responseHeadersContain).toBeInTheDocument(); expect(username).toBeInTheDocument(); - expect(username.value).toBe(defaultConfig[ConfigKeys.USERNAME]); + expect(username.value).toBe(defaultConfig[ConfigKey.USERNAME]); expect(password).toBeInTheDocument(); - expect(password.value).toBe(defaultConfig[ConfigKeys.PASSWORD]); + expect(password.value).toBe(defaultConfig[ConfigKey.PASSWORD]); }); it('handles changing fields', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx index 5299fa93e6dab9..c112d7da7b8cda 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx @@ -22,7 +22,7 @@ import { import { useHTTPAdvancedFieldsContext } from '../contexts'; -import { ConfigKeys, HTTPMethod, Validation } from '../types'; +import { ConfigKey, HTTPMethod, Validation } from '../types'; import { OptionalLabel } from '../optional_label'; import { HeaderField } from '../header_field'; @@ -37,7 +37,7 @@ interface Props { export const HTTPAdvancedFields = memo(({ validate }) => { const { fields, setFields } = useHTTPAdvancedFieldsContext(); const handleInputChange = useCallback( - ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }, [setFields] @@ -89,11 +89,11 @@ export const HTTPAdvancedFields = memo(({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.USERNAME, + configKey: ConfigKey.USERNAME, }) } data-test-subj="syntheticsUsername" @@ -115,11 +115,11 @@ export const HTTPAdvancedFields = memo(({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.PASSWORD, + configKey: ConfigKey.PASSWORD, }) } data-test-subj="syntheticsPassword" @@ -141,11 +141,11 @@ export const HTTPAdvancedFields = memo(({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.PROXY_URL, + configKey: ConfigKey.PROXY_URL, }) } data-test-subj="syntheticsProxyUrl" @@ -167,11 +167,11 @@ export const HTTPAdvancedFields = memo(({ validate }) => { > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.REQUEST_METHOD_CHECK, + configKey: ConfigKey.REQUEST_METHOD_CHECK, }) } data-test-subj="syntheticsRequestMethod" @@ -186,7 +186,7 @@ export const HTTPAdvancedFields = memo(({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.REQUEST_HEADERS_CHECK]?.(fields)} + isInvalid={!!validate[ConfigKey.REQUEST_HEADERS_CHECK]?.(fields)} error={ (({ validate }) => { > handleInputChange({ value, - configKey: ConfigKeys.REQUEST_HEADERS_CHECK, + configKey: ConfigKey.REQUEST_HEADERS_CHECK, }), [handleInputChange] )} @@ -235,13 +235,13 @@ export const HTTPAdvancedFields = memo(({ validate }) => { fullWidth > handleInputChange({ value, - configKey: ConfigKeys.REQUEST_BODY_CHECK, + configKey: ConfigKey.REQUEST_BODY_CHECK, }), [handleInputChange] )} @@ -280,7 +280,7 @@ export const HTTPAdvancedFields = memo(({ validate }) => { > (({ validate }) => { onChange={(event) => handleInputChange({ value: event.target.checked, - configKey: ConfigKeys.RESPONSE_HEADERS_INDEX, + configKey: ConfigKey.RESPONSE_HEADERS_INDEX, }) } /> @@ -307,10 +307,10 @@ export const HTTPAdvancedFields = memo(({ validate }) => { } > - handleInputChange({ value: policy, configKey: ConfigKeys.RESPONSE_BODY_INDEX }), + handleInputChange({ value: policy, configKey: ConfigKey.RESPONSE_BODY_INDEX }), [handleInputChange] )} /> @@ -340,7 +340,7 @@ export const HTTPAdvancedFields = memo(({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.RESPONSE_STATUS_CHECK]?.(fields)} + isInvalid={!!validate[ConfigKey.RESPONSE_STATUS_CHECK]?.(fields)} error={ (({ validate }) => { )} > handleInputChange({ value, - configKey: ConfigKeys.RESPONSE_STATUS_CHECK, + configKey: ConfigKey.RESPONSE_STATUS_CHECK, }) } data-test-subj="syntheticsResponseStatusCheck" @@ -375,7 +375,7 @@ export const HTTPAdvancedFields = memo(({ validate }) => { /> } labelAppend={} - isInvalid={!!validate[ConfigKeys.RESPONSE_HEADERS_CHECK]?.(fields)} + isInvalid={!!validate[ConfigKey.RESPONSE_HEADERS_CHECK]?.(fields)} error={[ (({ validate }) => { } > handleInputChange({ value, - configKey: ConfigKeys.RESPONSE_HEADERS_CHECK, + configKey: ConfigKey.RESPONSE_HEADERS_CHECK, }), [handleInputChange] )} @@ -419,12 +419,12 @@ export const HTTPAdvancedFields = memo(({ validate }) => { )} > handleInputChange({ value, - configKey: ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE, + configKey: ConfigKey.RESPONSE_BODY_CHECK_POSITIVE, }), [handleInputChange] )} @@ -448,12 +448,12 @@ export const HTTPAdvancedFields = memo(({ validate }) => { )} > handleInputChange({ value, - configKey: ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE, + configKey: ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE, }), [handleInputChange] )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/http/formatters.ts index 0c27eb3be1a2d6..a7440805b242c4 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/formatters.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { HTTPFields, ConfigKeys } from '../types'; +import { HTTPFields, ConfigKey } from '../types'; import { Formatter, commonFormatters, @@ -17,29 +17,29 @@ import { tlsFormatters } from '../tls/formatters'; export type HTTPFormatMap = Record; export const httpFormatters: HTTPFormatMap = { - [ConfigKeys.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKeys.METADATA]), - [ConfigKeys.URLS]: null, - [ConfigKeys.MAX_REDIRECTS]: null, - [ConfigKeys.USERNAME]: null, - [ConfigKeys.PASSWORD]: null, - [ConfigKeys.PROXY_URL]: null, - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: (fields) => - arrayToJsonFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]), - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: (fields) => - arrayToJsonFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]), - [ConfigKeys.RESPONSE_BODY_INDEX]: null, - [ConfigKeys.RESPONSE_HEADERS_CHECK]: (fields) => - objectToJsonFormatter(fields[ConfigKeys.RESPONSE_HEADERS_CHECK]), - [ConfigKeys.RESPONSE_HEADERS_INDEX]: null, - [ConfigKeys.RESPONSE_STATUS_CHECK]: (fields) => - arrayToJsonFormatter(fields[ConfigKeys.RESPONSE_STATUS_CHECK]), - [ConfigKeys.REQUEST_BODY_CHECK]: (fields) => - fields[ConfigKeys.REQUEST_BODY_CHECK]?.value - ? JSON.stringify(fields[ConfigKeys.REQUEST_BODY_CHECK]?.value) + [ConfigKey.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKey.METADATA]), + [ConfigKey.URLS]: null, + [ConfigKey.MAX_REDIRECTS]: null, + [ConfigKey.USERNAME]: null, + [ConfigKey.PASSWORD]: null, + [ConfigKey.PROXY_URL]: null, + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: (fields) => + arrayToJsonFormatter(fields[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]), + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: (fields) => + arrayToJsonFormatter(fields[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]), + [ConfigKey.RESPONSE_BODY_INDEX]: null, + [ConfigKey.RESPONSE_HEADERS_CHECK]: (fields) => + objectToJsonFormatter(fields[ConfigKey.RESPONSE_HEADERS_CHECK]), + [ConfigKey.RESPONSE_HEADERS_INDEX]: null, + [ConfigKey.RESPONSE_STATUS_CHECK]: (fields) => + arrayToJsonFormatter(fields[ConfigKey.RESPONSE_STATUS_CHECK]), + [ConfigKey.REQUEST_BODY_CHECK]: (fields) => + fields[ConfigKey.REQUEST_BODY_CHECK]?.value + ? JSON.stringify(fields[ConfigKey.REQUEST_BODY_CHECK]?.value) : null, - [ConfigKeys.REQUEST_HEADERS_CHECK]: (fields) => - objectToJsonFormatter(fields[ConfigKeys.REQUEST_HEADERS_CHECK]), - [ConfigKeys.REQUEST_METHOD_CHECK]: null, + [ConfigKey.REQUEST_HEADERS_CHECK]: (fields) => + objectToJsonFormatter(fields[ConfigKey.REQUEST_HEADERS_CHECK]), + [ConfigKey.REQUEST_METHOD_CHECK]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/http/normalizers.ts index e6e9b5121bf2ca..a4013a0e8024db 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/normalizers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { HTTPFields, ConfigKeys, ContentType, contentTypesToMode } from '../types'; +import { HTTPFields, ConfigKey, ContentType, contentTypesToMode } from '../types'; import { Normalizer, commonNormalizers, @@ -22,47 +22,47 @@ const defaultHTTPValues = { ...defaultHTTPAdvancedFields, }; -export const getHTTPNormalizer = (key: ConfigKeys) => { +export const getHTTPNormalizer = (key: ConfigKey) => { return getNormalizer(key, defaultHTTPValues); }; -export const getHTTPJsonToJavascriptNormalizer = (key: ConfigKeys) => { +export const getHTTPJsonToJavascriptNormalizer = (key: ConfigKey) => { return getJsonToJavascriptNormalizer(key, defaultHTTPValues); }; export const httpNormalizers: HTTPNormalizerMap = { - [ConfigKeys.METADATA]: getHTTPJsonToJavascriptNormalizer(ConfigKeys.METADATA), - [ConfigKeys.URLS]: getHTTPNormalizer(ConfigKeys.URLS), - [ConfigKeys.MAX_REDIRECTS]: getHTTPNormalizer(ConfigKeys.MAX_REDIRECTS), - [ConfigKeys.USERNAME]: getHTTPNormalizer(ConfigKeys.USERNAME), - [ConfigKeys.PASSWORD]: getHTTPNormalizer(ConfigKeys.PASSWORD), - [ConfigKeys.PROXY_URL]: getHTTPNormalizer(ConfigKeys.PROXY_URL), - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: getHTTPJsonToJavascriptNormalizer( - ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE + [ConfigKey.METADATA]: getHTTPJsonToJavascriptNormalizer(ConfigKey.METADATA), + [ConfigKey.URLS]: getHTTPNormalizer(ConfigKey.URLS), + [ConfigKey.MAX_REDIRECTS]: getHTTPNormalizer(ConfigKey.MAX_REDIRECTS), + [ConfigKey.USERNAME]: getHTTPNormalizer(ConfigKey.USERNAME), + [ConfigKey.PASSWORD]: getHTTPNormalizer(ConfigKey.PASSWORD), + [ConfigKey.PROXY_URL]: getHTTPNormalizer(ConfigKey.PROXY_URL), + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: getHTTPJsonToJavascriptNormalizer( + ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE ), - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: getHTTPJsonToJavascriptNormalizer( - ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: getHTTPJsonToJavascriptNormalizer( + ConfigKey.RESPONSE_BODY_CHECK_POSITIVE ), - [ConfigKeys.RESPONSE_BODY_INDEX]: getHTTPNormalizer(ConfigKeys.RESPONSE_BODY_INDEX), - [ConfigKeys.RESPONSE_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKeys.RESPONSE_HEADERS_CHECK + [ConfigKey.RESPONSE_BODY_INDEX]: getHTTPNormalizer(ConfigKey.RESPONSE_BODY_INDEX), + [ConfigKey.RESPONSE_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( + ConfigKey.RESPONSE_HEADERS_CHECK ), - [ConfigKeys.RESPONSE_HEADERS_INDEX]: getHTTPNormalizer(ConfigKeys.RESPONSE_HEADERS_INDEX), - [ConfigKeys.RESPONSE_STATUS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKeys.RESPONSE_STATUS_CHECK + [ConfigKey.RESPONSE_HEADERS_INDEX]: getHTTPNormalizer(ConfigKey.RESPONSE_HEADERS_INDEX), + [ConfigKey.RESPONSE_STATUS_CHECK]: getHTTPJsonToJavascriptNormalizer( + ConfigKey.RESPONSE_STATUS_CHECK ), - [ConfigKeys.REQUEST_BODY_CHECK]: (fields) => { - const requestBody = fields?.[ConfigKeys.REQUEST_BODY_CHECK]?.value; - const requestHeaders = fields?.[ConfigKeys.REQUEST_HEADERS_CHECK]?.value; + [ConfigKey.REQUEST_BODY_CHECK]: (fields) => { + const requestBody = fields?.[ConfigKey.REQUEST_BODY_CHECK]?.value; + const requestHeaders = fields?.[ConfigKey.REQUEST_HEADERS_CHECK]?.value; if (requestBody) { const headers = requestHeaders - ? JSON.parse(fields?.[ConfigKeys.REQUEST_HEADERS_CHECK]?.value) - : defaultHTTPAdvancedFields[ConfigKeys.REQUEST_HEADERS_CHECK]; + ? JSON.parse(fields?.[ConfigKey.REQUEST_HEADERS_CHECK]?.value) + : defaultHTTPAdvancedFields[ConfigKey.REQUEST_HEADERS_CHECK]; const requestBodyValue = requestBody !== null && requestBody !== undefined ? JSON.parse(requestBody) - : defaultHTTPAdvancedFields[ConfigKeys.REQUEST_BODY_CHECK]?.value; - let requestBodyType = defaultHTTPAdvancedFields[ConfigKeys.REQUEST_BODY_CHECK]?.type; + : defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]?.value; + let requestBodyType = defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]?.type; Object.keys(headers || []).some((headerKey) => { if (headerKey === 'Content-Type' && contentTypesToMode[headers[headerKey] as ContentType]) { requestBodyType = contentTypesToMode[headers[headerKey] as ContentType]; @@ -74,13 +74,13 @@ export const httpNormalizers: HTTPNormalizerMap = { type: requestBodyType, }; } else { - return defaultHTTPAdvancedFields[ConfigKeys.REQUEST_BODY_CHECK]; + return defaultHTTPAdvancedFields[ConfigKey.REQUEST_BODY_CHECK]; } }, - [ConfigKeys.REQUEST_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( - ConfigKeys.REQUEST_HEADERS_CHECK + [ConfigKey.REQUEST_HEADERS_CHECK]: getHTTPJsonToJavascriptNormalizer( + ConfigKey.REQUEST_HEADERS_CHECK ), - [ConfigKeys.REQUEST_METHOD_CHECK]: getHTTPNormalizer(ConfigKeys.REQUEST_METHOD_CHECK), + [ConfigKey.REQUEST_METHOD_CHECK]: getHTTPNormalizer(ConfigKey.REQUEST_METHOD_CHECK), ...commonNormalizers, ...tlsNormalizers, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx index 8d487a02229793..bda0c9fa590560 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx @@ -8,7 +8,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiFieldText, EuiFieldNumber } from '@elastic/eui'; -import { ConfigKeys, Validation } from '../types'; +import { ConfigKey, Validation } from '../types'; import { useHTTPSimpleFieldsContext } from '../contexts'; import { OptionalLabel } from '../optional_label'; import { ScheduleField } from '../schedule_field'; @@ -20,7 +20,7 @@ interface Props { export const HTTPSimpleFields = memo(({ validate }) => { const { fields, setFields } = useHTTPSimpleFieldsContext(); - const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }; @@ -33,7 +33,7 @@ export const HTTPSimpleFields = memo(({ validate }) => { defaultMessage="URL" /> } - isInvalid={!!validate[ConfigKeys.URLS]?.(fields)} + isInvalid={!!validate[ConfigKey.URLS]?.(fields)} error={ (({ validate }) => { } > - handleInputChange({ value: event.target.value, configKey: ConfigKeys.URLS }) + handleInputChange({ value: event.target.value, configKey: ConfigKey.URLS }) } data-test-subj="syntheticsUrlField" /> @@ -57,7 +57,7 @@ export const HTTPSimpleFields = memo(({ validate }) => { defaultMessage="Monitor interval" /> } - isInvalid={!!validate[ConfigKeys.SCHEDULE]?.(fields)} + isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ (({ validate }) => { onChange={(schedule) => handleInputChange({ value: schedule, - configKey: ConfigKeys.SCHEDULE, + configKey: ConfigKey.SCHEDULE, }) } - number={fields[ConfigKeys.SCHEDULE].number} - unit={fields[ConfigKeys.SCHEDULE].unit} + number={fields[ConfigKey.SCHEDULE].number} + unit={fields[ConfigKey.SCHEDULE].unit} /> (({ validate }) => { defaultMessage="Max redirects" /> } - isInvalid={!!validate[ConfigKeys.MAX_REDIRECTS]?.(fields)} + isInvalid={!!validate[ConfigKey.MAX_REDIRECTS]?.(fields)} error={ (({ validate }) => { > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.MAX_REDIRECTS, + configKey: ConfigKey.MAX_REDIRECTS, }) } /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/icmp/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/icmp/formatters.ts index 138df2b1e9ced9..6d249b6d5660c6 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/icmp/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/icmp/formatters.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { ICMPFields, ConfigKeys } from '../types'; +import { ICMPFields, ConfigKey } from '../types'; import { Formatter, commonFormatters, secondsToCronFormatter } from '../common/formatters'; export type ICMPFormatMap = Record; export const icmpFormatters: ICMPFormatMap = { - [ConfigKeys.HOSTS]: null, - [ConfigKeys.WAIT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.WAIT]), + [ConfigKey.HOSTS]: null, + [ConfigKey.WAIT]: (fields) => secondsToCronFormatter(fields[ConfigKey.WAIT]), ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/icmp/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/icmp/normalizers.ts index 18ce1da00e117e..e954d1f4f66e59 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/icmp/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/icmp/normalizers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ICMPFields, ConfigKeys } from '../types'; +import { ICMPFields, ConfigKey } from '../types'; import { Normalizer, commonNormalizers, @@ -16,16 +16,16 @@ import { defaultICMPSimpleFields } from '../contexts'; export type ICMPNormalizerMap = Record; -export const getICMPNormalizer = (key: ConfigKeys) => { +export const getICMPNormalizer = (key: ConfigKey) => { return getNormalizer(key, defaultICMPSimpleFields); }; -export const getICMPCronToSecondsNormalizer = (key: ConfigKeys) => { +export const getICMPCronToSecondsNormalizer = (key: ConfigKey) => { return getCronNormalizer(key, defaultICMPSimpleFields); }; export const icmpNormalizers: ICMPNormalizerMap = { - [ConfigKeys.HOSTS]: getICMPNormalizer(ConfigKeys.HOSTS), - [ConfigKeys.WAIT]: getICMPCronToSecondsNormalizer(ConfigKeys.WAIT), + [ConfigKey.HOSTS]: getICMPNormalizer(ConfigKey.HOSTS), + [ConfigKey.WAIT]: getICMPCronToSecondsNormalizer(ConfigKey.WAIT), ...commonNormalizers, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx index 9c605dacdfd223..20bd9b422ea0b6 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx @@ -8,7 +8,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiFieldText, EuiFieldNumber } from '@elastic/eui'; -import { ConfigKeys, Validation } from '../types'; +import { ConfigKey, Validation } from '../types'; import { useICMPSimpleFieldsContext } from '../contexts'; import { OptionalLabel } from '../optional_label'; import { ScheduleField } from '../schedule_field'; @@ -20,7 +20,7 @@ interface Props { export const ICMPSimpleFields = memo(({ validate }) => { const { fields, setFields } = useICMPSimpleFieldsContext(); - const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }; @@ -33,7 +33,7 @@ export const ICMPSimpleFields = memo(({ validate }) => { defaultMessage="Host" /> } - isInvalid={!!validate[ConfigKeys.HOSTS]?.(fields)} + isInvalid={!!validate[ConfigKey.HOSTS]?.(fields)} error={ (({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.HOSTS, + configKey: ConfigKey.HOSTS, }) } data-test-subj="syntheticsICMPHostField" @@ -60,7 +60,7 @@ export const ICMPSimpleFields = memo(({ validate }) => { defaultMessage="Monitor interval" /> } - isInvalid={!!validate[ConfigKeys.SCHEDULE]?.(fields)} + isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ (({ validate }) => { onChange={(schedule) => handleInputChange({ value: schedule, - configKey: ConfigKeys.SCHEDULE, + configKey: ConfigKey.SCHEDULE, }) } - number={fields[ConfigKeys.SCHEDULE].number} - unit={fields[ConfigKeys.SCHEDULE].unit} + number={fields[ConfigKey.SCHEDULE].number} + unit={fields[ConfigKey.SCHEDULE].unit} /> (({ validate }) => { defaultMessage="Wait in seconds" /> } - isInvalid={!!validate[ConfigKeys.WAIT]?.(fields)} + isInvalid={!!validate[ConfigKey.WAIT]?.(fields)} error={ (({ validate }) => { > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.WAIT, + configKey: ConfigKey.WAIT, }) } step={'any'} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx index 047d200d0af02d..267127c59e6dca 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx @@ -9,11 +9,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui'; -import { ConfigKeys, ICustomFields, ScheduleUnit } from './types'; +import { ConfigKey, MonitorFields, ScheduleUnit } from './types'; interface Props { number: string; - onChange: (schedule: ICustomFields[ConfigKeys.SCHEDULE]) => void; + onChange: (schedule: MonitorFields[ConfigKey.SCHEDULE]) => void; unit: ScheduleUnit; } diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx index 4fa101a329cd0a..db06f9dd8cd917 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx @@ -8,7 +8,8 @@ import React, { memo, useEffect, useMemo } from 'react'; import { PackagePolicyCreateExtensionComponentProps } from '../../../../fleet/public'; import { useTrackPageview } from '../../../../observability/public'; -import { PolicyConfig, DataStream } from './types'; +import { DataStream } from './types'; +import { PolicyConfig } from './types'; import { usePolicyConfigContext, defaultHTTPSimpleFields, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx index 138174792708fb..313fc460c5c5e7 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx @@ -13,7 +13,7 @@ import { render } from '../../lib/helper/rtl_helpers'; import { NewPackagePolicy } from '../../../../fleet/public'; import { SyntheticsPolicyCreateExtensionWrapper } from './synthetics_policy_create_extension_wrapper'; import { defaultConfig } from './synthetics_policy_create_extension'; -import { ConfigKeys, DataStream, ScheduleUnit, VerificationMode } from './types'; +import { ConfigKey, DataStream, ScheduleUnit, VerificationMode } from './types'; // ensures that fields appropriately match to their label jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ @@ -351,19 +351,19 @@ describe('', () => { expect(monitorType).toBeInTheDocument(); expect(monitorType.value).toEqual(DataStream.HTTP); expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKeys.URLS]); + expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKeys.MAX_REDIRECTS]}`); + expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Host')).not.toBeInTheDocument(); @@ -521,7 +521,7 @@ describe('', () => { const host = getByLabelText('Host:Port') as HTMLInputElement; expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultTCPConfig[ConfigKeys.HOSTS]); + expect(host.value).toEqual(defaultTCPConfig[ConfigKey.HOSTS]); // expect HTTP fields not to be in the DOM expect(queryByLabelText('URL')).not.toBeInTheDocument(); @@ -767,23 +767,23 @@ describe('', () => { await waitFor(() => { fireEvent.change(ca, { target: { value: 'certificateAuthorities' } }); - expect(ca.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]); + expect(ca.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]); }); await waitFor(() => { fireEvent.change(clientCertificate, { target: { value: 'clientCertificate' } }); - expect(clientCertificate.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_KEY]); + expect(clientCertificate.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_KEY]); }); await waitFor(() => { fireEvent.change(clientKey, { target: { value: 'clientKey' } }); - expect(clientKey.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_KEY]); + expect(clientKey.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_KEY]); }); await waitFor(() => { fireEvent.change(clientKeyPassphrase, { target: { value: 'clientKeyPassphrase' } }); - expect(clientKeyPassphrase.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_KEY_PASSPHRASE]); + expect(clientKeyPassphrase.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_KEY_PASSPHRASE]); }); await waitFor(() => { fireEvent.change(verificationMode, { target: { value: VerificationMode.NONE } }); - expect(verificationMode.value).toEqual(defaultHTTPConfig[ConfigKeys.TLS_VERIFICATION_MODE]); + expect(verificationMode.value).toEqual(defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]); }); await waitFor(() => { @@ -799,23 +799,23 @@ describe('', () => { ...defaultNewPolicy.inputs[0].streams[0], vars: { ...defaultNewPolicy.inputs[0].streams[0].vars, - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: { value: '"certificateAuthorities"', type: 'yaml', }, - [ConfigKeys.TLS_CERTIFICATE]: { + [ConfigKey.TLS_CERTIFICATE]: { value: '"clientCertificate"', type: 'yaml', }, - [ConfigKeys.TLS_KEY]: { + [ConfigKey.TLS_KEY]: { value: '"clientKey"', type: 'yaml', }, - [ConfigKeys.TLS_KEY_PASSPHRASE]: { + [ConfigKey.TLS_KEY_PASSPHRASE]: { value: 'clientKeyPassphrase', type: 'text', }, - [ConfigKeys.TLS_VERIFICATION_MODE]: { + [ConfigKey.TLS_VERIFICATION_MODE]: { value: VerificationMode.NONE, type: 'text', }, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx index 1e01f43439a314..db512537dfdb81 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { PackagePolicyEditExtensionComponentProps } from '../../../../fleet/public'; import { useTrackPageview } from '../../../../observability/public'; import { usePolicyConfigContext } from './contexts'; -import { ICustomFields, PolicyConfig } from './types'; +import { MonitorFields, PolicyConfig } from './types'; import { CustomFields } from './custom_fields'; import { useUpdatePolicy } from './hooks/use_update_policy'; import { usePolicy } from './hooks/use_policy'; @@ -18,7 +18,7 @@ import { validate } from './validation'; interface SyntheticsPolicyEditExtensionProps { newPolicy: PackagePolicyEditExtensionComponentProps['newPolicy']; onChange: PackagePolicyEditExtensionComponentProps['onChange']; - defaultConfig: Partial; + defaultConfig: Partial; } /** diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx index 51b7ddd90a0522..5ad02ef2b9a504 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx @@ -12,7 +12,7 @@ import { fireEvent, waitFor } from '@testing-library/react'; import { render } from '../../lib/helper/rtl_helpers'; import { NewPackagePolicy } from '../../../../fleet/public'; import { SyntheticsPolicyEditExtensionWrapper } from './synthetics_policy_edit_extension_wrapper'; -import { ConfigKeys, DataStream, ScheduleUnit } from './types'; +import { ConfigKey, DataStream, ScheduleUnit } from './types'; import { defaultConfig } from './synthetics_policy_create_extension'; // ensures that fields appropriately match to their label @@ -370,25 +370,23 @@ describe('', () => { const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKeys.URLS]); + expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKeys.MAX_REDIRECTS]}`); + expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); // expect TLS settings to be in the document when at least one tls key is populated expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('true'); expect(verificationMode).toBeInTheDocument(); - expect(verificationMode.value).toEqual( - `${defaultHTTPConfig[ConfigKeys.TLS_VERIFICATION_MODE]}` - ); + expect(verificationMode.value).toEqual(`${defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Host')).not.toBeInTheDocument(); @@ -606,7 +604,7 @@ describe('', () => { expect(enableTLSConfig.getAttribute('aria-checked')).toEqual('true'); expect(verificationMode).toBeInTheDocument(); expect(verificationMode.value).toEqual( - `${defaultHTTPConfig[ConfigKeys.TLS_VERIFICATION_MODE]}` + `${defaultHTTPConfig[ConfigKey.TLS_VERIFICATION_MODE]}` ); } ); @@ -839,7 +837,7 @@ describe('', () => { }; return acc; }, {}), - [ConfigKeys.MONITOR_TYPE]: { + [ConfigKey.MONITOR_TYPE]: { value: 'http', type: 'text', }, @@ -865,19 +863,19 @@ describe('', () => { const enableTLSConfig = getByLabelText('Enable TLS configuration') as HTMLInputElement; expect(url).toBeInTheDocument(); - expect(url.value).toEqual(defaultHTTPConfig[ConfigKeys.URLS]); + expect(url.value).toEqual(defaultHTTPConfig[ConfigKey.URLS]); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultHTTPConfig[ConfigKey.PROXY_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultHTTPConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultHTTPConfig[ConfigKey.APM_SERVICE_NAME]); expect(maxRedirects).toBeInTheDocument(); - expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKeys.MAX_REDIRECTS]}`); + expect(maxRedirects.value).toEqual(`${defaultHTTPConfig[ConfigKey.MAX_REDIRECTS]}`); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultHTTPConfig[ConfigKey.TIMEOUT]}`); /* expect TLS settings not to be in the document when and Enable TLS settings not to be checked * when all TLS values are falsey */ @@ -894,7 +892,7 @@ describe('', () => { await waitFor(() => { const requestMethod = getByLabelText('Request method') as HTMLInputElement; expect(requestMethod).toBeInTheDocument(); - expect(requestMethod.value).toEqual(`${defaultHTTPConfig[ConfigKeys.REQUEST_METHOD_CHECK]}`); + expect(requestMethod.value).toEqual(`${defaultHTTPConfig[ConfigKey.REQUEST_METHOD_CHECK]}`); }); }); @@ -923,7 +921,7 @@ describe('', () => { }; return acc; }, {}), - [ConfigKeys.MONITOR_TYPE]: { + [ConfigKey.MONITOR_TYPE]: { value: DataStream.TCP, type: 'text', }, @@ -944,17 +942,17 @@ describe('', () => { const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultTCPConfig[ConfigKeys.HOSTS]); + expect(host.value).toEqual(defaultTCPConfig[ConfigKey.HOSTS]); expect(proxyUrl).toBeInTheDocument(); - expect(proxyUrl.value).toEqual(defaultTCPConfig[ConfigKeys.PROXY_URL]); + expect(proxyUrl.value).toEqual(defaultTCPConfig[ConfigKey.PROXY_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultTCPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultTCPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultTCPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultTCPConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultTCPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultTCPConfig[ConfigKey.APM_SERVICE_NAME]); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultTCPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultTCPConfig[ConfigKey.TIMEOUT]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Url')).not.toBeInTheDocument(); @@ -997,7 +995,7 @@ describe('', () => { }; return acc; }, {}), - [ConfigKeys.MONITOR_TYPE]: { + [ConfigKey.MONITOR_TYPE]: { value: DataStream.ICMP, type: 'text', }, @@ -1017,17 +1015,17 @@ describe('', () => { const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; const wait = getByLabelText('Wait in seconds') as HTMLInputElement; expect(host).toBeInTheDocument(); - expect(host.value).toEqual(defaultICMPConfig[ConfigKeys.HOSTS]); + expect(host.value).toEqual(defaultICMPConfig[ConfigKey.HOSTS]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultICMPConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultICMPConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultICMPConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultICMPConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultICMPConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultICMPConfig[ConfigKey.APM_SERVICE_NAME]); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultICMPConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultICMPConfig[ConfigKey.TIMEOUT]}`); expect(wait).toBeInTheDocument(); - expect(wait.value).toEqual(`${defaultICMPConfig[ConfigKeys.WAIT]}`); + expect(wait.value).toEqual(`${defaultICMPConfig[ConfigKey.WAIT]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Url')).not.toBeInTheDocument(); @@ -1067,7 +1065,7 @@ describe('', () => { }; return acc; }, {}), - [ConfigKeys.MONITOR_TYPE]: { + [ConfigKey.MONITOR_TYPE]: { value: DataStream.BROWSER, type: 'text', }, @@ -1086,15 +1084,15 @@ describe('', () => { const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; expect(zipUrl).toBeInTheDocument(); - expect(zipUrl.value).toEqual(defaultBrowserConfig[ConfigKeys.SOURCE_ZIP_URL]); + expect(zipUrl.value).toEqual(defaultBrowserConfig[ConfigKey.SOURCE_ZIP_URL]); expect(monitorIntervalNumber).toBeInTheDocument(); - expect(monitorIntervalNumber.value).toEqual(defaultBrowserConfig[ConfigKeys.SCHEDULE].number); + expect(monitorIntervalNumber.value).toEqual(defaultBrowserConfig[ConfigKey.SCHEDULE].number); expect(monitorIntervalUnit).toBeInTheDocument(); - expect(monitorIntervalUnit.value).toEqual(defaultBrowserConfig[ConfigKeys.SCHEDULE].unit); + expect(monitorIntervalUnit.value).toEqual(defaultBrowserConfig[ConfigKey.SCHEDULE].unit); expect(apmServiceName).toBeInTheDocument(); - expect(apmServiceName.value).toEqual(defaultBrowserConfig[ConfigKeys.APM_SERVICE_NAME]); + expect(apmServiceName.value).toEqual(defaultBrowserConfig[ConfigKey.APM_SERVICE_NAME]); expect(timeout).toBeInTheDocument(); - expect(timeout.value).toEqual(`${defaultBrowserConfig[ConfigKeys.TIMEOUT]}`); + expect(timeout.value).toEqual(`${defaultBrowserConfig[ConfigKey.TIMEOUT]}`); // ensure other monitor type options are not in the DOM expect(queryByLabelText('Url')).not.toBeInTheDocument(); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx index 08f1f7e4f36718..ed676443202ab6 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx @@ -7,7 +7,8 @@ import React, { memo, useMemo } from 'react'; import { PackagePolicyEditExtensionComponentProps } from '../../../../fleet/public'; -import { PolicyConfig, ConfigKeys, DataStream, ITLSFields, ICustomFields } from './types'; +import { PolicyConfig, MonitorFields } from './types'; +import { ConfigKey, DataStream, TLSFields } from '././types'; import { SyntheticsPolicyEditExtension } from './synthetics_policy_edit_extension'; import { PolicyConfigContextProvider, @@ -46,36 +47,36 @@ export const SyntheticsPolicyEditExtensionWrapper = memo((acc: ICustomFields, key: ConfigKeys) => { + const configKeys: ConfigKey[] = Object.values(ConfigKey) || ([] as ConfigKey[]); + const formattedDefaultConfigForMonitorType: MonitorFields = + configKeys.reduce((acc: MonitorFields, key: ConfigKey) => { return { ...acc, [key]: normalizers[key]?.(vars), }; - }, {} as ICustomFields); + }, {} as MonitorFields); - const tlsConfig: ITLSFields = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES], - [ConfigKeys.TLS_CERTIFICATE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE], - [ConfigKeys.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY], - [ConfigKeys.TLS_KEY_PASSPHRASE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY_PASSPHRASE], - [ConfigKeys.TLS_VERIFICATION_MODE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERIFICATION_MODE], - [ConfigKeys.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERSION], + const tlsConfig: TLSFields = { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE_AUTHORITIES], + [ConfigKey.TLS_CERTIFICATE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE], + [ConfigKey.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY], + [ConfigKey.TLS_KEY_PASSPHRASE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY_PASSPHRASE], + [ConfigKey.TLS_VERIFICATION_MODE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERIFICATION_MODE], + [ConfigKey.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERSION], }; enableTLS = - formattedDefaultConfigForMonitorType[ConfigKeys.METADATA].is_tls_enabled || - Boolean(vars?.[ConfigKeys.TLS_VERIFICATION_MODE]?.value); + formattedDefaultConfigForMonitorType[ConfigKey.METADATA].is_tls_enabled || + Boolean(vars?.[ConfigKey.TLS_VERIFICATION_MODE]?.value); enableZipUrlTLS = - formattedDefaultConfigForMonitorType[ConfigKeys.METADATA].is_zip_url_tls_enabled || - Boolean(vars?.[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]?.value); + formattedDefaultConfigForMonitorType[ConfigKey.METADATA].is_zip_url_tls_enabled || + Boolean(vars?.[ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]?.value); const formattedDefaultConfig: Partial = { [type]: formattedDefaultConfigForMonitorType, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx index 78a6724fc8cfbf..b55fb733a8c869 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx @@ -13,7 +13,7 @@ import { TCPAdvancedFieldsContextProvider, defaultTCPAdvancedFields as defaultConfig, } from '../contexts'; -import { ConfigKeys, ITCPAdvancedFields } from '../types'; +import { ConfigKey, TCPAdvancedFields as TCPAdvancedFieldsType } from '../types'; // ensures fields and labels map appropriately jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ @@ -24,7 +24,7 @@ describe('', () => { const WrappedComponent = ({ defaultValues = defaultConfig, }: { - defaultValues?: ITCPAdvancedFields; + defaultValues?: TCPAdvancedFieldsType; }) => { return ( @@ -41,11 +41,11 @@ describe('', () => { // ComboBox has an issue with associating labels with the field const responseContains = getByLabelText('Check response contains') as HTMLInputElement; expect(requestPayload).toBeInTheDocument(); - expect(requestPayload.value).toEqual(defaultConfig[ConfigKeys.REQUEST_SEND_CHECK]); + expect(requestPayload.value).toEqual(defaultConfig[ConfigKey.REQUEST_SEND_CHECK]); expect(proxyURL).toBeInTheDocument(); - expect(proxyURL.value).toEqual(defaultConfig[ConfigKeys.PROXY_URL]); + expect(proxyURL.value).toEqual(defaultConfig[ConfigKey.PROXY_URL]); expect(responseContains).toBeInTheDocument(); - expect(responseContains.value).toEqual(defaultConfig[ConfigKeys.RESPONSE_RECEIVE_CHECK]); + expect(responseContains.value).toEqual(defaultConfig[ConfigKey.RESPONSE_RECEIVE_CHECK]); }); it('handles changing fields', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx index f96b5103a8edc3..8978fa92bb7f16 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx @@ -18,7 +18,7 @@ import { import { useTCPAdvancedFieldsContext } from '../contexts'; -import { ConfigKeys } from '../types'; +import { ConfigKey } from '../types'; import { OptionalLabel } from '../optional_label'; @@ -26,7 +26,7 @@ export const TCPAdvancedFields = () => { const { fields, setFields } = useTCPAdvancedFieldsContext(); const handleInputChange = useCallback( - ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }, [setFields] @@ -72,21 +72,21 @@ export const TCPAdvancedFields = () => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.PROXY_URL, + configKey: ConfigKey.PROXY_URL, }) } data-test-subj="syntheticsProxyUrl" /> - {!!fields[ConfigKeys.PROXY_URL] && ( + {!!fields[ConfigKey.PROXY_URL] && ( { onChange={(event) => handleInputChange({ value: event.target.checked, - configKey: ConfigKeys.PROXY_USE_LOCAL_RESOLVER, + configKey: ConfigKey.PROXY_USE_LOCAL_RESOLVER, }) } /> @@ -118,12 +118,12 @@ export const TCPAdvancedFields = () => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.REQUEST_SEND_CHECK, + configKey: ConfigKey.REQUEST_SEND_CHECK, }), [handleInputChange] )} @@ -163,12 +163,12 @@ export const TCPAdvancedFields = () => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.RESPONSE_RECEIVE_CHECK, + configKey: ConfigKey.RESPONSE_RECEIVE_CHECK, }), [handleInputChange] )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/tcp/formatters.ts index ce65c2c23d0d92..136edad6ce1983 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/formatters.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { TCPFields, ConfigKeys } from '../types'; +import { TCPFields, ConfigKey } from '../types'; import { Formatter, commonFormatters, objectToJsonFormatter } from '../common/formatters'; import { tlsFormatters } from '../tls/formatters'; export type TCPFormatMap = Record; export const tcpFormatters: TCPFormatMap = { - [ConfigKeys.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKeys.METADATA]), - [ConfigKeys.HOSTS]: null, - [ConfigKeys.PROXY_URL]: null, - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: null, - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: null, - [ConfigKeys.REQUEST_SEND_CHECK]: null, + [ConfigKey.METADATA]: (fields) => objectToJsonFormatter(fields[ConfigKey.METADATA]), + [ConfigKey.HOSTS]: null, + [ConfigKey.PROXY_URL]: null, + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: null, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: null, + [ConfigKey.REQUEST_SEND_CHECK]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/tcp/normalizers.ts index 962bb9cc9785e5..ae36de49fb57c2 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/normalizers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TCPFields, ConfigKeys } from '../types'; +import { TCPFields, ConfigKey } from '../types'; import { Normalizer, commonNormalizers, @@ -22,21 +22,21 @@ const defaultTCPFields = { export type TCPNormalizerMap = Record; -export const getTCPNormalizer = (key: ConfigKeys) => { +export const getTCPNormalizer = (key: ConfigKey) => { return getNormalizer(key, defaultTCPFields); }; -export const getTCPJsonToJavascriptNormalizer = (key: ConfigKeys) => { +export const getTCPJsonToJavascriptNormalizer = (key: ConfigKey) => { return getJsonToJavascriptNormalizer(key, defaultTCPFields); }; export const tcpNormalizers: TCPNormalizerMap = { - [ConfigKeys.METADATA]: getTCPJsonToJavascriptNormalizer(ConfigKeys.METADATA), - [ConfigKeys.HOSTS]: getTCPNormalizer(ConfigKeys.HOSTS), - [ConfigKeys.PROXY_URL]: getTCPNormalizer(ConfigKeys.PROXY_URL), - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: getTCPNormalizer(ConfigKeys.PROXY_USE_LOCAL_RESOLVER), - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: getTCPNormalizer(ConfigKeys.RESPONSE_RECEIVE_CHECK), - [ConfigKeys.REQUEST_SEND_CHECK]: getTCPNormalizer(ConfigKeys.REQUEST_SEND_CHECK), + [ConfigKey.METADATA]: getTCPJsonToJavascriptNormalizer(ConfigKey.METADATA), + [ConfigKey.HOSTS]: getTCPNormalizer(ConfigKey.HOSTS), + [ConfigKey.PROXY_URL]: getTCPNormalizer(ConfigKey.PROXY_URL), + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: getTCPNormalizer(ConfigKey.PROXY_USE_LOCAL_RESOLVER), + [ConfigKey.RESPONSE_RECEIVE_CHECK]: getTCPNormalizer(ConfigKey.RESPONSE_RECEIVE_CHECK), + [ConfigKey.REQUEST_SEND_CHECK]: getTCPNormalizer(ConfigKey.REQUEST_SEND_CHECK), ...tlsNormalizers, ...commonNormalizers, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx index 28ee223c5f2f87..cb6ebf6430eb84 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx @@ -8,7 +8,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiFieldText } from '@elastic/eui'; -import { ConfigKeys, Validation } from '../types'; +import { ConfigKey, Validation } from '../types'; import { useTCPSimpleFieldsContext } from '../contexts'; import { ScheduleField } from '../schedule_field'; import { CommonFields } from '../common/common_fields'; @@ -19,7 +19,7 @@ interface Props { export const TCPSimpleFields = memo(({ validate }) => { const { fields, setFields } = useTCPSimpleFieldsContext(); - const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKeys }) => { + const handleInputChange = ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }; @@ -32,7 +32,7 @@ export const TCPSimpleFields = memo(({ validate }) => { defaultMessage="Host:Port" /> } - isInvalid={!!validate[ConfigKeys.HOSTS]?.(fields)} + isInvalid={!!validate[ConfigKey.HOSTS]?.(fields)} error={ (({ validate }) => { } > handleInputChange({ value: event.target.value, - configKey: ConfigKeys.HOSTS, + configKey: ConfigKey.HOSTS, }) } data-test-subj="syntheticsTCPHostField" @@ -60,7 +60,7 @@ export const TCPSimpleFields = memo(({ validate }) => { defaultMessage="Monitor interval" /> } - isInvalid={!!validate[ConfigKeys.SCHEDULE]?.(fields)} + isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ (({ validate }) => { onChange={(schedule) => handleInputChange({ value: schedule, - configKey: ConfigKeys.SCHEDULE, + configKey: ConfigKey.SCHEDULE, }) } - number={fields[ConfigKeys.SCHEDULE].number} - unit={fields[ConfigKeys.SCHEDULE].unit} + number={fields[ConfigKey.SCHEDULE].number} + unit={fields[ConfigKey.SCHEDULE].unit} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tls/default_values.ts b/x-pack/plugins/uptime/public/components/fleet_package/tls/default_values.ts index 6f44e2c1c22b5e..18f291ce20f358 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tls/default_values.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/tls/default_values.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { ITLSFields, ConfigKeys, VerificationMode, TLSVersion } from '../types'; +import { TLSFields, ConfigKey, VerificationMode, TLSVersion } from '../types'; -export const defaultValues: ITLSFields = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: '', - [ConfigKeys.TLS_CERTIFICATE]: '', - [ConfigKeys.TLS_KEY]: '', - [ConfigKeys.TLS_KEY_PASSPHRASE]: '', - [ConfigKeys.TLS_VERIFICATION_MODE]: VerificationMode.FULL, - [ConfigKeys.TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO, TLSVersion.ONE_THREE], +export const defaultValues: TLSFields = { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: '', + [ConfigKey.TLS_CERTIFICATE]: '', + [ConfigKey.TLS_KEY]: '', + [ConfigKey.TLS_KEY_PASSPHRASE]: '', + [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.FULL, + [ConfigKey.TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO, TLSVersion.ONE_THREE], }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tls/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/tls/formatters.ts index 1191c7b0182372..c2ab88cbd49b23 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tls/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/tls/formatters.ts @@ -5,22 +5,22 @@ * 2.0. */ -import { ITLSFields, ConfigKeys } from '../types'; +import { TLSFields, ConfigKey } from '../types'; import { Formatter } from '../common/formatters'; -type TLSFormatMap = Record; +type TLSFormatMap = Record; export const tlsFormatters: TLSFormatMap = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: (fields) => - tlsValueToYamlFormatter(fields[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]), - [ConfigKeys.TLS_CERTIFICATE]: (fields) => - tlsValueToYamlFormatter(fields[ConfigKeys.TLS_CERTIFICATE]), - [ConfigKeys.TLS_KEY]: (fields) => tlsValueToYamlFormatter(fields[ConfigKeys.TLS_KEY]), - [ConfigKeys.TLS_KEY_PASSPHRASE]: (fields) => - tlsValueToStringFormatter(fields[ConfigKeys.TLS_KEY_PASSPHRASE]), - [ConfigKeys.TLS_VERIFICATION_MODE]: (fields) => - tlsValueToStringFormatter(fields[ConfigKeys.TLS_VERIFICATION_MODE]), - [ConfigKeys.TLS_VERSION]: (fields) => tlsArrayToYamlFormatter(fields[ConfigKeys.TLS_VERSION]), + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: (fields) => + tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]), + [ConfigKey.TLS_CERTIFICATE]: (fields) => + tlsValueToYamlFormatter(fields[ConfigKey.TLS_CERTIFICATE]), + [ConfigKey.TLS_KEY]: (fields) => tlsValueToYamlFormatter(fields[ConfigKey.TLS_KEY]), + [ConfigKey.TLS_KEY_PASSPHRASE]: (fields) => + tlsValueToStringFormatter(fields[ConfigKey.TLS_KEY_PASSPHRASE]), + [ConfigKey.TLS_VERIFICATION_MODE]: (fields) => + tlsValueToStringFormatter(fields[ConfigKey.TLS_VERIFICATION_MODE]), + [ConfigKey.TLS_VERSION]: (fields) => tlsArrayToYamlFormatter(fields[ConfigKey.TLS_VERSION]), }; // only add tls settings if they are enabled by the user and isEnabled is true diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tls/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/tls/normalizers.ts index 6398362220de1b..a4cf9f280e9a72 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tls/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/tls/normalizers.ts @@ -5,40 +5,40 @@ * 2.0. */ -import { ITLSFields, ConfigKeys } from '../types'; +import { TLSFields, ConfigKey } from '../types'; import { Normalizer } from '../common/normalizers'; import { defaultTLSFields } from '../contexts'; -type TLSNormalizerMap = Record; +type TLSNormalizerMap = Record; export const tlsNormalizers: TLSNormalizerMap = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: (fields) => + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: (fields) => tlsJsonToObjectNormalizer( - fields?.[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]?.value, - ConfigKeys.TLS_CERTIFICATE_AUTHORITIES + fields?.[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]?.value, + ConfigKey.TLS_CERTIFICATE_AUTHORITIES ), - [ConfigKeys.TLS_CERTIFICATE]: (fields) => + [ConfigKey.TLS_CERTIFICATE]: (fields) => tlsJsonToObjectNormalizer( - fields?.[ConfigKeys.TLS_CERTIFICATE]?.value, - ConfigKeys.TLS_CERTIFICATE + fields?.[ConfigKey.TLS_CERTIFICATE]?.value, + ConfigKey.TLS_CERTIFICATE ), - [ConfigKeys.TLS_KEY]: (fields) => - tlsJsonToObjectNormalizer(fields?.[ConfigKeys.TLS_KEY]?.value, ConfigKeys.TLS_KEY), - [ConfigKeys.TLS_KEY_PASSPHRASE]: (fields) => + [ConfigKey.TLS_KEY]: (fields) => + tlsJsonToObjectNormalizer(fields?.[ConfigKey.TLS_KEY]?.value, ConfigKey.TLS_KEY), + [ConfigKey.TLS_KEY_PASSPHRASE]: (fields) => tlsStringToObjectNormalizer( - fields?.[ConfigKeys.TLS_KEY_PASSPHRASE]?.value, - ConfigKeys.TLS_KEY_PASSPHRASE + fields?.[ConfigKey.TLS_KEY_PASSPHRASE]?.value, + ConfigKey.TLS_KEY_PASSPHRASE ), - [ConfigKeys.TLS_VERIFICATION_MODE]: (fields) => + [ConfigKey.TLS_VERIFICATION_MODE]: (fields) => tlsStringToObjectNormalizer( - fields?.[ConfigKeys.TLS_VERIFICATION_MODE]?.value, - ConfigKeys.TLS_VERIFICATION_MODE + fields?.[ConfigKey.TLS_VERIFICATION_MODE]?.value, + ConfigKey.TLS_VERIFICATION_MODE ), - [ConfigKeys.TLS_VERSION]: (fields) => - tlsJsonToObjectNormalizer(fields?.[ConfigKeys.TLS_VERSION]?.value, ConfigKeys.TLS_VERSION), + [ConfigKey.TLS_VERSION]: (fields) => + tlsJsonToObjectNormalizer(fields?.[ConfigKey.TLS_VERSION]?.value, ConfigKey.TLS_VERSION), }; -export const tlsStringToObjectNormalizer = (value: string = '', key: keyof ITLSFields) => +export const tlsStringToObjectNormalizer = (value: string = '', key: keyof TLSFields) => value ?? defaultTLSFields[key]; -export const tlsJsonToObjectNormalizer = (value: string = '', key: keyof ITLSFields) => +export const tlsJsonToObjectNormalizer = (value: string = '', key: keyof TLSFields) => value ? JSON.parse(value) : defaultTLSFields[key]; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.test.tsx index 1c9c4f4e69f439..8f38669976ef4f 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { render } from '../../lib/helper/rtl_helpers'; import { TLSFields } from './tls_fields'; -import { ConfigKeys, VerificationMode } from './types'; +import { ConfigKey, VerificationMode } from './types'; import { TLSFieldsContextProvider, PolicyConfigContextProvider, @@ -52,31 +52,31 @@ describe('', () => { const verificationMode = getByLabelText('Verification mode') as HTMLInputElement; const newValues = { - [ConfigKeys.TLS_CERTIFICATE]: 'sampleClientCertificate', - [ConfigKeys.TLS_KEY]: 'sampleClientKey', - [ConfigKeys.TLS_KEY_PASSPHRASE]: 'sampleClientKeyPassphrase', - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: 'sampleCertificateAuthorities', - [ConfigKeys.TLS_VERIFICATION_MODE]: VerificationMode.NONE, + [ConfigKey.TLS_CERTIFICATE]: 'sampleClientCertificate', + [ConfigKey.TLS_KEY]: 'sampleClientKey', + [ConfigKey.TLS_KEY_PASSPHRASE]: 'sampleClientKeyPassphrase', + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'sampleCertificateAuthorities', + [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.NONE, }; fireEvent.change(clientCertificate, { - target: { value: newValues[ConfigKeys.TLS_CERTIFICATE] }, + target: { value: newValues[ConfigKey.TLS_CERTIFICATE] }, }); - fireEvent.change(clientKey, { target: { value: newValues[ConfigKeys.TLS_KEY] } }); + fireEvent.change(clientKey, { target: { value: newValues[ConfigKey.TLS_KEY] } }); fireEvent.change(clientKeyPassphrase, { - target: { value: newValues[ConfigKeys.TLS_KEY_PASSPHRASE] }, + target: { value: newValues[ConfigKey.TLS_KEY_PASSPHRASE] }, }); fireEvent.change(certificateAuthorities, { - target: { value: newValues[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES] }, + target: { value: newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES] }, }); fireEvent.change(verificationMode, { - target: { value: newValues[ConfigKeys.TLS_VERIFICATION_MODE] }, + target: { value: newValues[ConfigKey.TLS_VERIFICATION_MODE] }, }); - expect(clientCertificate.value).toEqual(newValues[ConfigKeys.TLS_CERTIFICATE]); - expect(clientKey.value).toEqual(newValues[ConfigKeys.TLS_KEY]); - expect(certificateAuthorities.value).toEqual(newValues[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]); - expect(verificationMode.value).toEqual(newValues[ConfigKeys.TLS_VERIFICATION_MODE]); + expect(clientCertificate.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE]); + expect(clientKey.value).toEqual(newValues[ConfigKey.TLS_KEY]); + expect(certificateAuthorities.value).toEqual(newValues[ConfigKey.TLS_CERTIFICATE_AUTHORITIES]); + expect(verificationMode.value).toEqual(newValues[ConfigKey.TLS_VERIFICATION_MODE]); }); it('shows warning when verification mode is set to none', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.tsx index e33b4ef3fbb1b6..8b627929731155 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tls_fields.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect } from 'react'; import { TLSOptions, TLSConfig } from './common/tls_options'; import { useTLSFieldsContext, usePolicyConfigContext } from './contexts'; -import { ConfigKeys } from './types'; +import { ConfigKey } from './types'; export const TLSFields = () => { const { defaultValues, setFields } = useTLSFieldsContext(); @@ -19,12 +19,12 @@ export const TLSFields = () => { const handleOnChange = useCallback( (tlsConfig: TLSConfig) => { setFields({ - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: tlsConfig.certificateAuthorities, - [ConfigKeys.TLS_CERTIFICATE]: tlsConfig.certificate, - [ConfigKeys.TLS_KEY]: tlsConfig.key, - [ConfigKeys.TLS_KEY_PASSPHRASE]: tlsConfig.keyPassphrase, - [ConfigKeys.TLS_VERIFICATION_MODE]: tlsConfig.verificationMode, - [ConfigKeys.TLS_VERSION]: tlsConfig.version, + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: tlsConfig.certificateAuthorities, + [ConfigKey.TLS_CERTIFICATE]: tlsConfig.certificate, + [ConfigKey.TLS_KEY]: tlsConfig.key, + [ConfigKey.TLS_KEY_PASSPHRASE]: tlsConfig.keyPassphrase, + [ConfigKey.TLS_VERIFICATION_MODE]: tlsConfig.verificationMode, + [ConfigKey.TLS_VERSION]: tlsConfig.version, }); }, [setFields] @@ -33,12 +33,12 @@ export const TLSFields = () => { useEffect(() => { if (!isTLSEnabled) { setFields({ - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKeys.TLS_CERTIFICATE]: undefined, - [ConfigKeys.TLS_KEY]: undefined, - [ConfigKeys.TLS_KEY_PASSPHRASE]: undefined, - [ConfigKeys.TLS_VERIFICATION_MODE]: undefined, - [ConfigKeys.TLS_VERSION]: undefined, + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: undefined, + [ConfigKey.TLS_CERTIFICATE]: undefined, + [ConfigKey.TLS_KEY]: undefined, + [ConfigKey.TLS_KEY_PASSPHRASE]: undefined, + [ConfigKey.TLS_VERIFICATION_MODE]: undefined, + [ConfigKey.TLS_VERSION]: undefined, }); } }, [setFields, isTLSEnabled]); @@ -46,12 +46,12 @@ export const TLSFields = () => { return isTLSEnabled ? ( ; - [ConfigKeys.RESPONSE_HEADERS_INDEX]: boolean; - [ConfigKeys.RESPONSE_STATUS_CHECK]: string[]; - [ConfigKeys.REQUEST_BODY_CHECK]: { value: string; type: Mode }; - [ConfigKeys.REQUEST_HEADERS_CHECK]: Record; - [ConfigKeys.REQUEST_METHOD_CHECK]: string; - [ConfigKeys.USERNAME]: string; -} - -export interface ITCPAdvancedFields { - [ConfigKeys.PROXY_URL]: string; - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: boolean; - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: string; - [ConfigKeys.REQUEST_SEND_CHECK]: string; -} - -export type IBrowserSimpleFields = { - [ConfigKeys.METADATA]: Metadata; - [ConfigKeys.SOURCE_INLINE]: string; - [ConfigKeys.SOURCE_ZIP_URL]: string; - [ConfigKeys.SOURCE_ZIP_FOLDER]: string; - [ConfigKeys.SOURCE_ZIP_USERNAME]: string; - [ConfigKeys.SOURCE_ZIP_PASSWORD]: string; - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: string; - [ConfigKeys.PARAMS]: string; -} & ICommonFields & - IZipUrlTLSFields; - -export interface IBrowserAdvancedFields { - [ConfigKeys.SYNTHETICS_ARGS]: string[]; - [ConfigKeys.SCREENSHOTS]: string; - [ConfigKeys.JOURNEY_FILTERS_MATCH]: string; - [ConfigKeys.JOURNEY_FILTERS_TAGS]: string[]; - [ConfigKeys.IGNORE_HTTPS_ERRORS]: boolean; - [ConfigKeys.IS_THROTTLING_ENABLED]: boolean; - [ConfigKeys.DOWNLOAD_SPEED]: string; - [ConfigKeys.UPLOAD_SPEED]: string; - [ConfigKeys.LATENCY]: string; - [ConfigKeys.THROTTLING_CONFIG]: string; -} - -export type HTTPFields = IHTTPSimpleFields & IHTTPAdvancedFields & ITLSFields; -export type TCPFields = ITCPSimpleFields & ITCPAdvancedFields & ITLSFields; -export type ICMPFields = IICMPSimpleFields; -export type BrowserFields = IBrowserSimpleFields & IBrowserAdvancedFields; - -export type ICustomFields = HTTPFields & - TCPFields & - ICMPFields & - BrowserFields & { - [ConfigKeys.NAME]: string; - }; - -export type Monitor = Partial; +import { + HTTPFields, + TCPFields, + ICMPFields, + BrowserFields, + ConfigKey, + ContentType, + DataStream, + MonitorFields, + Mode, + ThrottlingConfigKey, + ThrottlingSuffix, + ThrottlingSuffixType, +} from '../../../common/runtime_types/monitor_management'; +export * from '../../../common/runtime_types/monitor_management'; + +export type Monitor = Partial; export interface PolicyConfig { [DataStream.HTTP]: HTTPFields; @@ -256,9 +29,9 @@ export interface PolicyConfig { [DataStream.BROWSER]: BrowserFields; } -export type Validator = (config: Partial) => boolean; +export type Validator = (config: Partial) => boolean; -export type Validation = Partial>; +export type Validation = Partial>; export const contentTypesToMode = { [ContentType.FORM]: Mode.FORM, @@ -267,13 +40,8 @@ export const contentTypesToMode = { [ContentType.XML]: Mode.XML, }; -export type ThrottlingConfigKey = - | ConfigKeys.DOWNLOAD_SPEED - | ConfigKeys.UPLOAD_SPEED - | ConfigKeys.LATENCY; - -export const configKeyToThrottlingSuffix: Record = { - [ConfigKeys.DOWNLOAD_SPEED]: ThrottlingSuffix.DOWNLOAD, - [ConfigKeys.UPLOAD_SPEED]: ThrottlingSuffix.UPLOAD, - [ConfigKeys.LATENCY]: ThrottlingSuffix.LATENCY, +export const configKeyToThrottlingSuffix: Record = { + [ConfigKey.DOWNLOAD_SPEED]: ThrottlingSuffix.DOWNLOAD, + [ConfigKey.UPLOAD_SPEED]: ThrottlingSuffix.UPLOAD, + [ConfigKey.LATENCY]: ThrottlingSuffix.LATENCY, }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx b/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx index 5191297119440e..96611d8305f89e 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/validation.tsx @@ -4,14 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { - ConfigKeys, - DataStream, - ICustomFields, - Validator, - Validation, - ScheduleUnit, -} from './types'; +import { ConfigKey, DataStream, ScheduleUnit, MonitorFields, Validator, Validation } from './types'; export const digitsOnly = /^[0-9]*$/g; export const includesValidPort = /[^\:]+:[0-9]{1,5}$/g; @@ -57,13 +50,13 @@ const validateTimeout = ({ // validation functions return true when invalid const validateCommon: ValidationLibrary = { - [ConfigKeys.SCHEDULE]: ({ [ConfigKeys.SCHEDULE]: value }) => { - const { number, unit } = value as ICustomFields[ConfigKeys.SCHEDULE]; + [ConfigKey.SCHEDULE]: ({ [ConfigKey.SCHEDULE]: value }) => { + const { number, unit } = value as MonitorFields[ConfigKey.SCHEDULE]; const parsedFloat = parseFloat(number); return !parsedFloat || !unit || parsedFloat < 1; }, - [ConfigKeys.TIMEOUT]: ({ [ConfigKeys.TIMEOUT]: timeout, [ConfigKeys.SCHEDULE]: schedule }) => { - const { number, unit } = schedule as ICustomFields[ConfigKeys.SCHEDULE]; + [ConfigKey.TIMEOUT]: ({ [ConfigKey.TIMEOUT]: timeout, [ConfigKey.SCHEDULE]: schedule }) => { + const { number, unit } = schedule as MonitorFields[ConfigKey.SCHEDULE]; return ( !timeout || @@ -78,38 +71,38 @@ const validateCommon: ValidationLibrary = { }; const validateHTTP: ValidationLibrary = { - [ConfigKeys.RESPONSE_STATUS_CHECK]: ({ [ConfigKeys.RESPONSE_STATUS_CHECK]: value }) => { - const statusCodes = value as ICustomFields[ConfigKeys.RESPONSE_STATUS_CHECK]; + [ConfigKey.RESPONSE_STATUS_CHECK]: ({ [ConfigKey.RESPONSE_STATUS_CHECK]: value }) => { + const statusCodes = value as MonitorFields[ConfigKey.RESPONSE_STATUS_CHECK]; return statusCodes.length ? statusCodes.some((code) => !`${code}`.match(digitsOnly)) : false; }, - [ConfigKeys.RESPONSE_HEADERS_CHECK]: ({ [ConfigKeys.RESPONSE_HEADERS_CHECK]: value }) => { - const headers = value as ICustomFields[ConfigKeys.RESPONSE_HEADERS_CHECK]; - return validateHeaders(headers); + [ConfigKey.RESPONSE_HEADERS_CHECK]: ({ [ConfigKey.RESPONSE_HEADERS_CHECK]: value }) => { + const headers = value as MonitorFields[ConfigKey.RESPONSE_HEADERS_CHECK]; + return validateHeaders(headers); }, - [ConfigKeys.REQUEST_HEADERS_CHECK]: ({ [ConfigKeys.REQUEST_HEADERS_CHECK]: value }) => { - const headers = value as ICustomFields[ConfigKeys.REQUEST_HEADERS_CHECK]; - return validateHeaders(headers); + [ConfigKey.REQUEST_HEADERS_CHECK]: ({ [ConfigKey.REQUEST_HEADERS_CHECK]: value }) => { + const headers = value as MonitorFields[ConfigKey.REQUEST_HEADERS_CHECK]; + return validateHeaders(headers); }, - [ConfigKeys.MAX_REDIRECTS]: ({ [ConfigKeys.MAX_REDIRECTS]: value }) => + [ConfigKey.MAX_REDIRECTS]: ({ [ConfigKey.MAX_REDIRECTS]: value }) => (!!value && !`${value}`.match(digitsOnly)) || - parseFloat(value as ICustomFields[ConfigKeys.MAX_REDIRECTS]) < 0, - [ConfigKeys.URLS]: ({ [ConfigKeys.URLS]: value }) => !value, + parseFloat(value as MonitorFields[ConfigKey.MAX_REDIRECTS]) < 0, + [ConfigKey.URLS]: ({ [ConfigKey.URLS]: value }) => !value, ...validateCommon, }; const validateTCP: Record = { - [ConfigKeys.HOSTS]: ({ [ConfigKeys.HOSTS]: value }) => { + [ConfigKey.HOSTS]: ({ [ConfigKey.HOSTS]: value }) => { return !value || !`${value}`.match(includesValidPort); }, ...validateCommon, }; const validateICMP: ValidationLibrary = { - [ConfigKeys.HOSTS]: ({ [ConfigKeys.HOSTS]: value }) => !value, - [ConfigKeys.WAIT]: ({ [ConfigKeys.WAIT]: value }) => + [ConfigKey.HOSTS]: ({ [ConfigKey.HOSTS]: value }) => !value, + [ConfigKey.WAIT]: ({ [ConfigKey.WAIT]: value }) => !!value && !digitsOnly.test(`${value}`) && - parseFloat(value as ICustomFields[ConfigKeys.WAIT]) < 0, + parseFloat(value as MonitorFields[ConfigKey.WAIT]) < 0, ...validateCommon, }; @@ -121,19 +114,19 @@ const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) = const validateBrowser: ValidationLibrary = { ...validateCommon, - [ConfigKeys.SOURCE_ZIP_URL]: ({ - [ConfigKeys.SOURCE_ZIP_URL]: zipUrl, - [ConfigKeys.SOURCE_INLINE]: inlineScript, + [ConfigKey.SOURCE_ZIP_URL]: ({ + [ConfigKey.SOURCE_ZIP_URL]: zipUrl, + [ConfigKey.SOURCE_INLINE]: inlineScript, }) => !zipUrl && !inlineScript, - [ConfigKeys.SOURCE_INLINE]: ({ - [ConfigKeys.SOURCE_ZIP_URL]: zipUrl, - [ConfigKeys.SOURCE_INLINE]: inlineScript, + [ConfigKey.SOURCE_INLINE]: ({ + [ConfigKey.SOURCE_ZIP_URL]: zipUrl, + [ConfigKey.SOURCE_INLINE]: inlineScript, }) => !zipUrl && !inlineScript, - [ConfigKeys.DOWNLOAD_SPEED]: ({ [ConfigKeys.DOWNLOAD_SPEED]: downloadSpeed }) => + [ConfigKey.DOWNLOAD_SPEED]: ({ [ConfigKey.DOWNLOAD_SPEED]: downloadSpeed }) => validateThrottleValue(downloadSpeed), - [ConfigKeys.UPLOAD_SPEED]: ({ [ConfigKeys.UPLOAD_SPEED]: uploadSpeed }) => + [ConfigKey.UPLOAD_SPEED]: ({ [ConfigKey.UPLOAD_SPEED]: uploadSpeed }) => validateThrottleValue(uploadSpeed), - [ConfigKeys.LATENCY]: ({ [ConfigKeys.LATENCY]: latency }) => validateThrottleValue(latency, true), + [ConfigKey.LATENCY]: ({ [ConfigKey.LATENCY]: latency }) => validateThrottleValue(latency, true), }; export type ValidateDictionary = Record; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts index a4af2333333f69..a2792e7e8e3178 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/browser.ts @@ -5,36 +5,36 @@ * 2.0. */ -import { BrowserFields, ConfigKeys } from '../../fleet_package/types'; +import { BrowserFields, ConfigKey } from '../../fleet_package/types'; import { Formatter, commonFormatters, objectFormatter, arrayFormatter } from './common'; export type BrowserFormatMap = Record; export const browserFormatters: BrowserFormatMap = { - [ConfigKeys.METADATA]: (fields) => objectFormatter(fields[ConfigKeys.METADATA]), - [ConfigKeys.SOURCE_ZIP_URL]: null, - [ConfigKeys.SOURCE_ZIP_USERNAME]: null, - [ConfigKeys.SOURCE_ZIP_PASSWORD]: null, - [ConfigKeys.SOURCE_ZIP_FOLDER]: null, - [ConfigKeys.SOURCE_ZIP_PROXY_URL]: null, - [ConfigKeys.SOURCE_INLINE]: null, - [ConfigKeys.PARAMS]: null, - [ConfigKeys.SCREENSHOTS]: null, - [ConfigKeys.SYNTHETICS_ARGS]: (fields) => null, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: null, - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: null, - [ConfigKeys.ZIP_URL_TLS_KEY]: null, - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: null, - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: null, - [ConfigKeys.IS_THROTTLING_ENABLED]: null, - [ConfigKeys.THROTTLING_CONFIG]: null, - [ConfigKeys.DOWNLOAD_SPEED]: null, - [ConfigKeys.UPLOAD_SPEED]: null, - [ConfigKeys.LATENCY]: null, - [ConfigKeys.ZIP_URL_TLS_VERSION]: (fields) => - arrayFormatter(fields[ConfigKeys.ZIP_URL_TLS_VERSION]), - [ConfigKeys.JOURNEY_FILTERS_MATCH]: null, - [ConfigKeys.JOURNEY_FILTERS_TAGS]: null, - [ConfigKeys.IGNORE_HTTPS_ERRORS]: null, + [ConfigKey.METADATA]: (fields) => objectFormatter(fields[ConfigKey.METADATA]), + [ConfigKey.SOURCE_ZIP_URL]: null, + [ConfigKey.SOURCE_ZIP_USERNAME]: null, + [ConfigKey.SOURCE_ZIP_PASSWORD]: null, + [ConfigKey.SOURCE_ZIP_FOLDER]: null, + [ConfigKey.SOURCE_ZIP_PROXY_URL]: null, + [ConfigKey.SOURCE_INLINE]: null, + [ConfigKey.PARAMS]: null, + [ConfigKey.SCREENSHOTS]: null, + [ConfigKey.SYNTHETICS_ARGS]: (fields) => null, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: null, + [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: null, + [ConfigKey.ZIP_URL_TLS_KEY]: null, + [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: null, + [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: null, + [ConfigKey.IS_THROTTLING_ENABLED]: null, + [ConfigKey.THROTTLING_CONFIG]: null, + [ConfigKey.DOWNLOAD_SPEED]: null, + [ConfigKey.UPLOAD_SPEED]: null, + [ConfigKey.LATENCY]: null, + [ConfigKey.ZIP_URL_TLS_VERSION]: (fields) => + arrayFormatter(fields[ConfigKey.ZIP_URL_TLS_VERSION]), + [ConfigKey.JOURNEY_FILTERS_MATCH]: null, + [ConfigKey.JOURNEY_FILTERS_TAGS]: null, + [ConfigKey.IGNORE_HTTPS_ERRORS]: null, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/common.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/common.ts index 92d2b28f1283d1..da50dfd4b08604 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/common.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/common.ts @@ -5,22 +5,22 @@ * 2.0. */ -import { ICommonFields, ICustomFields, ConfigKeys } from '../../fleet_package/types'; +import { CommonFields, MonitorFields, ConfigKey } from '../../fleet_package/types'; export type Formatter = | null - | ((fields: Partial) => string | string[] | Record | null); + | ((fields: Partial) => string | string[] | Record | null); -export type CommonFormatMap = Record; +export type CommonFormatMap = Record; export const commonFormatters: CommonFormatMap = { - [ConfigKeys.NAME]: null, - [ConfigKeys.MONITOR_TYPE]: null, - [ConfigKeys.SCHEDULE]: (fields) => - `@every ${fields[ConfigKeys.SCHEDULE]?.number}${fields[ConfigKeys.SCHEDULE]?.unit}`, - [ConfigKeys.APM_SERVICE_NAME]: null, - [ConfigKeys.TAGS]: null, - [ConfigKeys.TIMEOUT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.TIMEOUT]), + [ConfigKey.NAME]: null, + [ConfigKey.MONITOR_TYPE]: null, + [ConfigKey.SCHEDULE]: (fields) => + `@every ${fields[ConfigKey.SCHEDULE]?.number}${fields[ConfigKey.SCHEDULE]?.unit}`, + [ConfigKey.APM_SERVICE_NAME]: null, + [ConfigKey.TAGS]: null, + [ConfigKey.TIMEOUT]: (fields) => secondsToCronFormatter(fields[ConfigKey.TIMEOUT]), }; export const arrayFormatter = (value: string[] = []) => (value.length ? value : null); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/http.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/http.ts index 41e162cff2d05d..67a8bed8dc8b90 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/http.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/http.ts @@ -5,33 +5,33 @@ * 2.0. */ -import { HTTPFields, ConfigKeys } from '../../fleet_package/types'; +import { HTTPFields, ConfigKey } from '../../fleet_package/types'; import { Formatter, commonFormatters, objectFormatter, arrayFormatter } from './common'; import { tlsFormatters } from './tls'; export type HTTPFormatMap = Record; export const httpFormatters: HTTPFormatMap = { - [ConfigKeys.METADATA]: (fields) => objectFormatter(fields[ConfigKeys.METADATA]), - [ConfigKeys.URLS]: null, - [ConfigKeys.MAX_REDIRECTS]: null, - [ConfigKeys.USERNAME]: null, - [ConfigKeys.PASSWORD]: null, - [ConfigKeys.PROXY_URL]: null, - [ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]: (fields) => - arrayFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_NEGATIVE]), - [ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]: (fields) => - arrayFormatter(fields[ConfigKeys.RESPONSE_BODY_CHECK_POSITIVE]), - [ConfigKeys.RESPONSE_BODY_INDEX]: null, - [ConfigKeys.RESPONSE_HEADERS_CHECK]: (fields) => - objectFormatter(fields[ConfigKeys.RESPONSE_HEADERS_CHECK]), - [ConfigKeys.RESPONSE_HEADERS_INDEX]: null, - [ConfigKeys.RESPONSE_STATUS_CHECK]: (fields) => - arrayFormatter(fields[ConfigKeys.RESPONSE_STATUS_CHECK]), - [ConfigKeys.REQUEST_BODY_CHECK]: (fields) => fields[ConfigKeys.REQUEST_BODY_CHECK]?.value || null, - [ConfigKeys.REQUEST_HEADERS_CHECK]: (fields) => - objectFormatter(fields[ConfigKeys.REQUEST_HEADERS_CHECK]), - [ConfigKeys.REQUEST_METHOD_CHECK]: null, + [ConfigKey.METADATA]: (fields) => objectFormatter(fields[ConfigKey.METADATA]), + [ConfigKey.URLS]: null, + [ConfigKey.MAX_REDIRECTS]: null, + [ConfigKey.USERNAME]: null, + [ConfigKey.PASSWORD]: null, + [ConfigKey.PROXY_URL]: null, + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: (fields) => + arrayFormatter(fields[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]), + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: (fields) => + arrayFormatter(fields[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]), + [ConfigKey.RESPONSE_BODY_INDEX]: null, + [ConfigKey.RESPONSE_HEADERS_CHECK]: (fields) => + objectFormatter(fields[ConfigKey.RESPONSE_HEADERS_CHECK]), + [ConfigKey.RESPONSE_HEADERS_INDEX]: null, + [ConfigKey.RESPONSE_STATUS_CHECK]: (fields) => + arrayFormatter(fields[ConfigKey.RESPONSE_STATUS_CHECK]), + [ConfigKey.REQUEST_BODY_CHECK]: (fields) => fields[ConfigKey.REQUEST_BODY_CHECK]?.value || null, + [ConfigKey.REQUEST_HEADERS_CHECK]: (fields) => + objectFormatter(fields[ConfigKey.REQUEST_HEADERS_CHECK]), + [ConfigKey.REQUEST_METHOD_CHECK]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/icmp.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/icmp.ts index 841f9863094829..891ee07c3a2458 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/icmp.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/icmp.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { ICMPFields, ConfigKeys } from '../../fleet_package/types'; +import { ICMPFields, ConfigKey } from '../../fleet_package/types'; import { Formatter, commonFormatters, secondsToCronFormatter } from './common'; export type ICMPFormatMap = Record; export const icmpFormatters: ICMPFormatMap = { - [ConfigKeys.HOSTS]: null, - [ConfigKeys.WAIT]: (fields) => secondsToCronFormatter(fields[ConfigKeys.WAIT]), + [ConfigKey.HOSTS]: null, + [ConfigKey.WAIT]: (fields) => secondsToCronFormatter(fields[ConfigKey.WAIT]), ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/tcp.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/tcp.ts index cdf77307a6938d..065ac693bfcef1 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/tcp.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/tcp.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { TCPFields, ConfigKeys } from '../../fleet_package/types'; +import { TCPFields, ConfigKey } from '../../fleet_package/types'; import { Formatter, commonFormatters } from './common'; import { tlsFormatters } from './tls'; export type TCPFormatMap = Record; export const tcpFormatters: TCPFormatMap = { - [ConfigKeys.METADATA]: null, - [ConfigKeys.HOSTS]: null, - [ConfigKeys.PROXY_URL]: null, - [ConfigKeys.PROXY_USE_LOCAL_RESOLVER]: null, - [ConfigKeys.RESPONSE_RECEIVE_CHECK]: null, - [ConfigKeys.REQUEST_SEND_CHECK]: null, + [ConfigKey.METADATA]: null, + [ConfigKey.HOSTS]: null, + [ConfigKey.PROXY_URL]: null, + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: null, + [ConfigKey.RESPONSE_RECEIVE_CHECK]: null, + [ConfigKey.REQUEST_SEND_CHECK]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/formatters/tls.ts b/x-pack/plugins/uptime/public/components/monitor_management/formatters/tls.ts index 5f7ce27637f3e2..9de353677649de 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/formatters/tls.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/formatters/tls.ts @@ -5,16 +5,16 @@ * 2.0. */ -import { ITLSFields, ConfigKeys } from '../../fleet_package/types'; +import { TLSFields, ConfigKey } from '../../fleet_package/types'; import { arrayFormatter, Formatter } from './common'; -type TLSFormatMap = Record; +type TLSFormatMap = Record; export const tlsFormatters: TLSFormatMap = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: null, - [ConfigKeys.TLS_CERTIFICATE]: null, - [ConfigKeys.TLS_KEY]: null, - [ConfigKeys.TLS_KEY_PASSPHRASE]: null, - [ConfigKeys.TLS_VERIFICATION_MODE]: null, - [ConfigKeys.TLS_VERSION]: (fields) => arrayFormatter(fields[ConfigKeys.TLS_VERSION]), + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: null, + [ConfigKey.TLS_CERTIFICATE]: null, + [ConfigKey.TLS_KEY]: null, + [ConfigKey.TLS_KEY_PASSPHRASE]: null, + [ConfigKey.TLS_VERIFICATION_MODE]: null, + [ConfigKey.TLS_VERSION]: (fields) => arrayFormatter(fields[ConfigKey.TLS_VERSION]), }; diff --git a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_format_monitor.ts b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_format_monitor.ts index 9027aa45204184..c895965700c458 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_format_monitor.ts +++ b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_format_monitor.ts @@ -6,18 +6,18 @@ */ import { useEffect, useRef, useState } from 'react'; import { omitBy, isNil } from 'lodash'; -import { ConfigKeys, DataStream, Validation, ICustomFields } from '../../fleet_package/types'; +import { ConfigKey, DataStream, Validation, MonitorFields } from '../../fleet_package/types'; import { formatters } from '../formatters'; interface Props { monitorType: DataStream; - defaultConfig: Partial; - config: Partial; + defaultConfig: Partial; + config: Partial; validate: Record; } -const formatMonitorConfig = (configKeys: ConfigKeys[], config: Partial) => { - const formattedMonitor = {} as Record; +const formatMonitorConfig = (configKeys: ConfigKey[], config: Partial) => { + const formattedMonitor = {} as Record; configKeys.forEach((key) => { const value = config[key] ?? null; @@ -28,19 +28,19 @@ const formatMonitorConfig = (configKeys: ConfigKeys[], config: Partial; + return omitBy(formattedMonitor, isNil) as Partial; }; export const useFormatMonitor = ({ monitorType, defaultConfig, config, validate }: Props) => { - const [formattedMonitor, setFormattedMonitor] = useState>( - formatMonitorConfig(Object.keys(config) as ConfigKeys[], config) + const [formattedMonitor, setFormattedMonitor] = useState>( + formatMonitorConfig(Object.keys(config) as ConfigKey[], config) ); const [isValid, setIsValid] = useState(false); - const currentConfig = useRef>(defaultConfig); + const currentConfig = useRef>(defaultConfig); useEffect(() => { - const configKeys = Object.keys(config) as ConfigKeys[]; - const validationKeys = Object.keys(validate[monitorType]) as ConfigKeys[]; + const configKeys = Object.keys(config) as ConfigKey[]; + const validationKeys = Object.keys(validate[monitorType]) as ConfigKey[]; const configDidUpdate = configKeys.some((key) => config[key] !== currentConfig.current[key]); const isValidT = !!config.name && !validationKeys.find((key) => validate[monitorType]?.[key]?.(config)); diff --git a/x-pack/plugins/uptime/public/pages/edit_monitor.tsx b/x-pack/plugins/uptime/public/pages/edit_monitor.tsx index 965639bd121452..724d9cdc44fc8c 100644 --- a/x-pack/plugins/uptime/public/pages/edit_monitor.tsx +++ b/x-pack/plugins/uptime/public/pages/edit_monitor.tsx @@ -7,9 +7,9 @@ import React, { useMemo } from 'react'; import { - ConfigKeys, - ICustomFields, - ITLSFields, + ConfigKey, + MonitorFields, + TLSFields, PolicyConfig, DataStream, } from '../components/fleet_package/types'; @@ -30,40 +30,40 @@ export const EditMonitorPage: React.FC = () => { tlsConfig: defaultTLSConfig, } = useMemo(() => { /* TODO: fetch current monitor to be edited from saved objects based on url param */ - const monitor = {} as Record; // fetch + const monitor = {} as Record; // fetch let enableTLS = false; let enableZipUrlTLS = false; const getDefaultConfig = () => { - const type: DataStream = monitor[ConfigKeys.MONITOR_TYPE] as DataStream; + const type: DataStream = monitor[ConfigKey.MONITOR_TYPE] as DataStream; - const configKeys: ConfigKeys[] = Object.values(ConfigKeys) || ([] as ConfigKeys[]); - const formattedDefaultConfigForMonitorType: ICustomFields = configKeys.reduce( - (acc: ICustomFields, key: ConfigKeys) => { + const configKeys: ConfigKey[] = Object.values(ConfigKey) || ([] as ConfigKey[]); + const formattedDefaultConfigForMonitorType: MonitorFields = configKeys.reduce( + (acc: MonitorFields, key: ConfigKey) => { return { ...acc, key, }; }, - {} as ICustomFields + {} as MonitorFields ); - const tlsConfig: ITLSFields = { - [ConfigKeys.TLS_CERTIFICATE_AUTHORITIES]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES], - [ConfigKeys.TLS_CERTIFICATE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_CERTIFICATE], - [ConfigKeys.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY], - [ConfigKeys.TLS_KEY_PASSPHRASE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_KEY_PASSPHRASE], - [ConfigKeys.TLS_VERIFICATION_MODE]: - formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERIFICATION_MODE], - [ConfigKeys.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERSION], + const tlsConfig: TLSFields = { + [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE_AUTHORITIES], + [ConfigKey.TLS_CERTIFICATE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_CERTIFICATE], + [ConfigKey.TLS_KEY]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY], + [ConfigKey.TLS_KEY_PASSPHRASE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_KEY_PASSPHRASE], + [ConfigKey.TLS_VERIFICATION_MODE]: + formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERIFICATION_MODE], + [ConfigKey.TLS_VERSION]: formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERSION], }; - enableTLS = Boolean(formattedDefaultConfigForMonitorType[ConfigKeys.TLS_VERIFICATION_MODE]); + enableTLS = Boolean(formattedDefaultConfigForMonitorType[ConfigKey.TLS_VERIFICATION_MODE]); enableZipUrlTLS = Boolean( - formattedDefaultConfigForMonitorType[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE] + formattedDefaultConfigForMonitorType[ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE] ); const formattedDefaultConfig: Partial = {