From cec92ff199212b1acecea44718ead1f506da5b12 Mon Sep 17 00:00:00 2001 From: Bailey Cash Date: Wed, 19 Feb 2025 08:53:33 -0500 Subject: [PATCH 1/5] introduce new spaces field for synthetics api keys --- .../common/runtime_types/settings/api_key.ts | 14 ++ .../settings/components/spaces_select.tsx | 25 ++- .../private_locations/location_form.tsx | 2 +- .../project_api_keys.test.tsx | 1 - .../project_api_keys/project_api_keys.tsx | 153 +++++++++++------- .../state/monitor_management/api.ts | 4 +- .../public/utils/api_service/api_service.ts | 5 +- .../routes/monitor_cruds/get_api_key.ts | 4 +- .../server/synthetics_service/get_api_key.ts | 4 +- 9 files changed, 138 insertions(+), 74 deletions(-) create mode 100644 x-pack/solutions/observability/plugins/synthetics/common/runtime_types/settings/api_key.ts diff --git a/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/settings/api_key.ts b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/settings/api_key.ts new file mode 100644 index 0000000000000..d67710539053f --- /dev/null +++ b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/settings/api_key.ts @@ -0,0 +1,14 @@ +/* + * 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'; + +export const APIKeyCodec = t.type({ + spaces: t.array(t.string), +}); + +export type SyntheticsProjectAPIKey = t.TypeOf; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx index ce002a189c8ef..aa9f19557cd1a 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx @@ -9,24 +9,28 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { EuiComboBox, EuiFormRow } from '@elastic/eui'; -import { Controller, useFormContext } from 'react-hook-form'; +import { Controller, FieldValues, Path, useFormContext } from 'react-hook-form'; import { ALL_SPACES_ID } from '@kbn/security-plugin/public'; import { ClientPluginsStart } from '../../../../../plugin'; -import { PrivateLocation } from '../../../../../../common/runtime_types'; -export const NAMESPACES_NAME = 'spaces'; +interface SpaceSelectorProps { + module: 'location' | 'apiKey'; +} -export const SpaceSelector: React.FC = () => { +export const SpaceSelector = ({ module }: SpaceSelectorProps) => { + const NAMESPACES_NAME = 'spaces' as Path; const { services } = useKibana(); const [spacesList, setSpacesList] = React.useState>([]); const data = services.spaces?.ui.useSpaces(); + const HELP_TEXT = module === 'location' ? LOCATION_HELP_TEXT : API_KEY_HELP_TEXT; + const { control, formState: { isSubmitted }, trigger, - } = useFormContext(); + } = useFormContext(); const { isTouched, error } = control.getFieldState(NAMESPACES_NAME); const showFieldInvalid = (isSubmitted || isTouched) && !!error; @@ -122,6 +126,13 @@ const SPACES_LABEL = i18n.translate('xpack.synthetics.privateLocation.spacesLabe defaultMessage: 'Spaces ', }); -const HELP_TEXT = i18n.translate('xpack.synthetics.privateLocation.spacesHelpText', { - defaultMessage: 'Select the spaces where this location will be available.', +const LOCATION_HELP_TEXT = i18n.translate( + 'xpack.synthetics.privateLocation.locationSpacesHelpText', + { + defaultMessage: 'Select the spaces where this location will be available.', + } +); + +const API_KEY_HELP_TEXT = i18n.translate('xpack.synthetics.privateLocation.apiKeySpacesHelpText', { + defaultMessage: 'Select the spaces where this API key will be available.', }); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx index ee58ed2165e42..1ffcc6c1bd5a0 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx @@ -62,7 +62,7 @@ export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLo - + ); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.test.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.test.tsx index 90eef39022817..0cee0a6dc5a07 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.test.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.test.tsx @@ -45,7 +45,6 @@ describe('', () => { }); it('shows appropriate content when user does not have correct uptime save permissions', () => { - // const apiKey = 'sampleApiKey'; render(, { state, core: makeUptimePermissionsCore({ save: false }), diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx index 4a8752e8b9926..3332a88e0e6e0 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx @@ -4,28 +4,53 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { EuiText, EuiLink, EuiEmptyPrompt, EuiSwitch, EuiSpacer } from '@elastic/eui'; +import { EuiText, EuiLink, EuiEmptyPrompt, EuiSwitch, EuiSpacer, EuiForm } from '@elastic/eui'; +import { SpacesContextProps } from '@kbn/spaces-plugin/public'; import { i18n } from '@kbn/i18n'; import { useFetcher } from '@kbn/observability-shared-plugin/public'; import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; +import { ALL_SPACES_ID } from '@kbn/security-plugin/public'; +import { FormProvider } from 'react-hook-form'; import { HelpCommands } from './help_commands'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; import { fetchProjectAPIKey } from '../../../state/monitor_management/api'; import { ClientPluginsStart } from '../../../../../plugin'; import { ApiKeyBtn } from './api_key_btn'; import { useEnablement } from '../../../hooks'; +import { SpaceSelector } from '../components/spaces_select'; +import { useFormWrapped } from '../../../../../hooks/use_form_wrapped'; const syntheticsTestRunDocsLink = 'https://www.elastic.co/guide/en/observability/current/synthetic-run-tests.html'; +const getEmptyFunctionComponent: React.FC = ({ children }) => <>{children}; + export const ProjectAPIKeys = () => { const { loading: enablementLoading, canManageApiKeys } = useEnablement(); const [apiKey, setApiKey] = useState(undefined); const [loadAPIKey, setLoadAPIKey] = useState(false); const [accessToElasticManagedLocations, setAccessToElasticManagedLocations] = useState(true); + const { spaces: spacesApi } = useKibana().services; + + const ContextWrapper = useMemo( + () => + spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, + [spacesApi] + ); + + const form = useFormWrapped({ + mode: 'onSubmit', + reValidateMode: 'onChange', + shouldFocusError: true, + defaultValues: { + apiKey, + spaces: [ALL_SPACES_ID], + }, + }); + const kServices = useKibana().services; const canSaveIntegrations: boolean = !!kServices?.fleet?.authz.integrations.writeIntegrationPolicies; @@ -35,7 +60,10 @@ export const ProjectAPIKeys = () => { const { data, loading, error } = useFetcher(async () => { if (loadAPIKey) { - return fetchProjectAPIKey(accessToElasticManagedLocations && Boolean(canUsePublicLocations)); + return fetchProjectAPIKey( + accessToElasticManagedLocations && Boolean(canUsePublicLocations), + form.getValues()?.spaces + ); } return null; // FIXME: Dario thinks there is a better way to do this but @@ -69,64 +97,67 @@ export const ProjectAPIKeys = () => { } return ( - <> - {GET_API_KEY_GENERATE}} - body={ - canSave && canManageApiKeys ? ( - <> - - {GET_API_KEY_LABEL_DESCRIPTION}{' '} - {!canSaveIntegrations ? `${API_KEY_DISCLAIMER} ` : ''} - - {LEARN_MORE_LABEL} - - - - { - setAccessToElasticManagedLocations(!accessToElasticManagedLocations); - }} - disabled={!canUsePublicLocations} - /> - - ) : ( - <> - - {GET_API_KEY_REDUCED_PERMISSIONS_LABEL}{' '} - - {LEARN_MORE_LABEL} - - - - ) - } - actions={ - - } - /> - {apiKey && } - + + + {GET_API_KEY_GENERATE}} + body={ + canSave && canManageApiKeys ? ( + + + {GET_API_KEY_LABEL_DESCRIPTION}{' '} + {!canSaveIntegrations ? `${API_KEY_DISCLAIMER} ` : ''} + + {LEARN_MORE_LABEL} + + + + { + setAccessToElasticManagedLocations(!accessToElasticManagedLocations); + }} + disabled={!canUsePublicLocations} + /> + + + ) : ( + <> + + {GET_API_KEY_REDUCED_PERMISSIONS_LABEL}{' '} + + {LEARN_MORE_LABEL} + + + + ) + } + actions={ + + } + /> + {apiKey && } + + ); }; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index 6ee33a03b9df7..f92e042b545b2 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -63,10 +63,12 @@ export const updateMonitorAPI = async ({ }; export const fetchProjectAPIKey = async ( - accessToElasticManagedLocations: boolean + accessToElasticManagedLocations: boolean, + spaces: string[] ): Promise => { return await apiService.get(SYNTHETICS_API_URLS.SYNTHETICS_PROJECT_APIKEY, { accessToElasticManagedLocations, + spaces, }); }; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts b/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts index d16e34b430f1c..f8a8ea35b219d 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts @@ -88,7 +88,10 @@ class ApiService { const { version, spaceId, ...queryParams } = params; const response = await this._http!.fetch({ path: this.parseApiUrl(apiUrl, spaceId), - query: queryParams, + query: { + ...queryParams, + spaces: queryParams.spaces ? JSON.stringify(queryParams.spaces) : undefined, + }, version, ...(options ?? {}), ...(spaceId ? { prependBasePath: false } : {}), diff --git a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/get_api_key.ts b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/get_api_key.ts index 8e4f06f904dc3..a0ce0c2145381 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/get_api_key.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/routes/monitor_cruds/get_api_key.ts @@ -21,6 +21,7 @@ export const getAPIKeySyntheticsRoute: SyntheticsRestApiRouteFactory = () => ({ path: SYNTHETICS_API_URLS.SYNTHETICS_PROJECT_APIKEY, validate: { query: schema.object({ + spaces: schema.maybe(schema.arrayOf(schema.string())), accessToElasticManagedLocations: schema.maybe(schema.boolean()), }), }, @@ -29,7 +30,7 @@ export const getAPIKeySyntheticsRoute: SyntheticsRestApiRouteFactory = () => ({ server, response, }): Promise => { - const { accessToElasticManagedLocations } = request.query; + const { accessToElasticManagedLocations, spaces } = request.query; if (accessToElasticManagedLocations) { const elasticManagedLocationsEnabled = @@ -52,6 +53,7 @@ export const getAPIKeySyntheticsRoute: SyntheticsRestApiRouteFactory = () => ({ request, server, accessToElasticManagedLocations, + spaces, }); return { apiKey }; diff --git a/x-pack/solutions/observability/plugins/synthetics/server/synthetics_service/get_api_key.ts b/x-pack/solutions/observability/plugins/synthetics/server/synthetics_service/get_api_key.ts index 46b96f32b2a4a..b43f1356b098c 100644 --- a/x-pack/solutions/observability/plugins/synthetics/server/synthetics_service/get_api_key.ts +++ b/x-pack/solutions/observability/plugins/synthetics/server/synthetics_service/get_api_key.ts @@ -117,10 +117,12 @@ export const generateProjectAPIKey = async ({ server, request, accessToElasticManagedLocations = true, + spaces = [ALL_SPACES_ID], }: { server: SyntheticsServerSetup; request: KibanaRequest; accessToElasticManagedLocations?: boolean; + spaces?: string[]; }): Promise => { const { security } = server; const isApiKeysEnabled = await security.authc.apiKeys?.areAPIKeysEnabled(); @@ -138,7 +140,7 @@ export const generateProjectAPIKey = async ({ kibana: [ { base: [], - spaces: [ALL_SPACES_ID], + spaces, feature: { uptime: [accessToElasticManagedLocations ? 'all' : 'minimal_all'], }, From 2c944e55dca17db408e6e5c7188aa7f19b0de229 Mon Sep 17 00:00:00 2001 From: Bailey Cash Date: Wed, 19 Feb 2025 15:52:14 -0500 Subject: [PATCH 2/5] fix validation issue --- .../settings/components/spaces_select.tsx | 19 ++------- .../private_locations/location_form.tsx | 9 +++- .../settings/project_api_keys/api_key_btn.tsx | 8 ++-- .../project_api_keys/project_api_keys.tsx | 42 ++++++++++++------- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx index aa9f19557cd1a..51fecfef7f94e 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/components/spaces_select.tsx @@ -15,17 +15,15 @@ import { ALL_SPACES_ID } from '@kbn/security-plugin/public'; import { ClientPluginsStart } from '../../../../../plugin'; interface SpaceSelectorProps { - module: 'location' | 'apiKey'; + helpText: string; } -export const SpaceSelector = ({ module }: SpaceSelectorProps) => { +export const SpaceSelector = ({ helpText }: SpaceSelectorProps) => { const NAMESPACES_NAME = 'spaces' as Path; const { services } = useKibana(); const [spacesList, setSpacesList] = React.useState>([]); const data = services.spaces?.ui.useSpaces(); - const HELP_TEXT = module === 'location' ? LOCATION_HELP_TEXT : API_KEY_HELP_TEXT; - const { control, formState: { isSubmitted }, @@ -53,7 +51,7 @@ export const SpaceSelector = ({ module }: SpaceSelectorPr @@ -125,14 +123,3 @@ const allSpacesOption = { const SPACES_LABEL = i18n.translate('xpack.synthetics.privateLocation.spacesLabel', { defaultMessage: 'Spaces ', }); - -const LOCATION_HELP_TEXT = i18n.translate( - 'xpack.synthetics.privateLocation.locationSpacesHelpText', - { - defaultMessage: 'Select the spaces where this location will be available.', - } -); - -const API_KEY_HELP_TEXT = i18n.translate('xpack.synthetics.privateLocation.apiKeySpacesHelpText', { - defaultMessage: 'Select the spaces where this API key will be available.', -}); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx index 1ffcc6c1bd5a0..48b4b1e6dbad8 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/location_form.tsx @@ -62,7 +62,7 @@ export const LocationForm = ({ privateLocations }: { privateLocations: PrivateLo - + ); @@ -95,6 +95,13 @@ export const LOCATION_NAME_LABEL = i18n.translate( } ); +const LOCATION_HELP_TEXT = i18n.translate( + 'xpack.synthetics.privateLocation.locationSpacesHelpText', + { + defaultMessage: 'Select the spaces where this location will be available.', + } +); + const NAME_ALREADY_EXISTS = i18n.translate('xpack.synthetics.monitorManagement.alreadyExists', { defaultMessage: 'Location name already exists.', }); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.tsx index 6cbf3760f3e08..e206a9f0ea053 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.tsx @@ -12,12 +12,12 @@ export const ApiKeyBtn = ({ isDisabled, apiKey, loading, - setLoadAPIKey, + onClick: callback, }: { loading?: boolean; isDisabled?: boolean; apiKey?: string; - setLoadAPIKey: (val: boolean) => void; + onClick: Function; }) => { return ( <> @@ -30,9 +30,7 @@ export const ApiKeyBtn = ({ fullWidth={true} isLoading={loading} color="primary" - onClick={() => { - setLoadAPIKey(true); - }} + onClick={() => callback()} data-test-subj="uptimeMonitorManagementApiKeyGenerate" > {loading ? GET_API_KEY_LOADING_LABEL : GET_API_KEY_LABEL} diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx index 3332a88e0e6e0..71064382e240b 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/project_api_keys.tsx @@ -13,18 +13,18 @@ import { useFetcher } from '@kbn/observability-shared-plugin/public'; import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; import { ALL_SPACES_ID } from '@kbn/security-plugin/public'; import { FormProvider } from 'react-hook-form'; +import { SyntheticsProjectAPIKey } from '../../../../../../common/runtime_types/settings/api_key'; import { HelpCommands } from './help_commands'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; import { fetchProjectAPIKey } from '../../../state/monitor_management/api'; import { ClientPluginsStart } from '../../../../../plugin'; -import { ApiKeyBtn } from './api_key_btn'; import { useEnablement } from '../../../hooks'; import { SpaceSelector } from '../components/spaces_select'; import { useFormWrapped } from '../../../../../hooks/use_form_wrapped'; +import { ApiKeyBtn } from './api_key_btn'; const syntheticsTestRunDocsLink = 'https://www.elastic.co/guide/en/observability/current/synthetic-run-tests.html'; - const getEmptyFunctionComponent: React.FC = ({ children }) => <>{children}; export const ProjectAPIKeys = () => { @@ -33,24 +33,26 @@ export const ProjectAPIKeys = () => { const [loadAPIKey, setLoadAPIKey] = useState(false); const [accessToElasticManagedLocations, setAccessToElasticManagedLocations] = useState(true); - const { spaces: spacesApi } = useKibana().services; - - const ContextWrapper = useMemo( - () => - spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, - [spacesApi] - ); - const form = useFormWrapped({ mode: 'onSubmit', reValidateMode: 'onChange', shouldFocusError: true, defaultValues: { - apiKey, spaces: [ALL_SPACES_ID], }, }); + const { handleSubmit } = form; + + const { spaces: spacesApi } = useKibana().services; + const spaces = useMemo(() => form.getValues()?.spaces, [form]); + + const ContextWrapper = useMemo( + () => + spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, + [spacesApi] + ); + const kServices = useKibana().services; const canSaveIntegrations: boolean = !!kServices?.fleet?.authz.integrations.writeIntegrationPolicies; @@ -62,14 +64,20 @@ export const ProjectAPIKeys = () => { if (loadAPIKey) { return fetchProjectAPIKey( accessToElasticManagedLocations && Boolean(canUsePublicLocations), - form.getValues()?.spaces + spaces ); } return null; // FIXME: Dario thinks there is a better way to do this but // he's getting tired and maybe the Synthetics folks can fix it // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loadAPIKey, canUsePublicLocations]); + }, [loadAPIKey, canUsePublicLocations, spaces]); + + const onSubmit = (formData: SyntheticsProjectAPIKey) => { + if (formData.spaces?.length) { + setLoadAPIKey(true); + } + }; useEffect(() => { if (data?.apiKey) { @@ -128,7 +136,7 @@ export const ProjectAPIKeys = () => { }} disabled={!canUsePublicLocations} /> - + ) : ( <> @@ -149,7 +157,7 @@ export const ProjectAPIKeys = () => { actions={ @@ -194,3 +202,7 @@ const GET_API_KEY_REDUCED_PERMISSIONS_LABEL = i18n.translate( 'Use an API key to push monitors remotely from a CLI or CD pipeline. To generate an API key, you must have permissions to manage API keys and Uptime write access. Please contact your administrator.', } ); + +const API_KEY_HELP_TEXT = i18n.translate('xpack.synthetics.privateLocation.apiKeySpacesHelpText', { + defaultMessage: 'Select the spaces where this API key will be available.', +}); From 5c6f6281767e1117ff1ba29e9f3282edd532c794 Mon Sep 17 00:00:00 2001 From: Bailey Cash Date: Wed, 19 Feb 2025 16:29:06 -0500 Subject: [PATCH 3/5] fix tests --- .../settings/project_api_keys/api_key_btn.test.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx index 1053fc4e5f731..5d8bfd2381ded 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx @@ -14,8 +14,10 @@ import { render } from '../../../utils/testing'; describe('', () => { const setLoadAPIKey = jest.fn(); + const clickCallback = jest.fn(); + it('calls delete monitor on monitor deletion', async () => { - render(); + render(); expect(screen.getByText('Generate Project API key')).toBeInTheDocument(); await userEvent.click(screen.getByTestId('uptimeMonitorManagementApiKeyGenerate')); @@ -23,14 +25,14 @@ describe('', () => { }); it('shows correct content on loading', () => { - render(); + render(); expect(screen.getByText('Generating API key')).toBeInTheDocument(); }); it('shows api key when available and hides button', () => { const apiKey = 'sampleApiKey'; - render(); + render(); expect(screen.queryByText('Generate Project API key')).not.toBeInTheDocument(); }); From 144c6f7796c4ac533d15cffc471f6b03b7a979c2 Mon Sep 17 00:00:00 2001 From: Bailey Cash Date: Wed, 19 Feb 2025 17:00:32 -0500 Subject: [PATCH 4/5] preserve abstraction --- .../public/apps/synthetics/state/monitor_management/api.ts | 2 +- .../synthetics/public/utils/api_service/api_service.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts index f92e042b545b2..ae2d4c0dfd93a 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_management/api.ts @@ -68,7 +68,7 @@ export const fetchProjectAPIKey = async ( ): Promise => { return await apiService.get(SYNTHETICS_API_URLS.SYNTHETICS_PROJECT_APIKEY, { accessToElasticManagedLocations, - spaces, + spaces: JSON.stringify(spaces), }); }; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts b/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts index f8a8ea35b219d..d16e34b430f1c 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/utils/api_service/api_service.ts @@ -88,10 +88,7 @@ class ApiService { const { version, spaceId, ...queryParams } = params; const response = await this._http!.fetch({ path: this.parseApiUrl(apiUrl, spaceId), - query: { - ...queryParams, - spaces: queryParams.spaces ? JSON.stringify(queryParams.spaces) : undefined, - }, + query: queryParams, version, ...(options ?? {}), ...(spaceId ? { prependBasePath: false } : {}), From 36dd211f8333acb82ad8761dceba24560e7fcfb7 Mon Sep 17 00:00:00 2001 From: Bailey Cash Date: Thu, 20 Feb 2025 11:24:30 -0500 Subject: [PATCH 5/5] test fix? --- .../components/settings/project_api_keys/api_key_btn.test.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx index 5d8bfd2381ded..13697ec94f77d 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/settings/project_api_keys/api_key_btn.test.tsx @@ -12,8 +12,6 @@ import { ApiKeyBtn } from './api_key_btn'; import { render } from '../../../utils/testing'; describe('', () => { - const setLoadAPIKey = jest.fn(); - const clickCallback = jest.fn(); it('calls delete monitor on monitor deletion', async () => { @@ -21,7 +19,7 @@ describe('', () => { expect(screen.getByText('Generate Project API key')).toBeInTheDocument(); await userEvent.click(screen.getByTestId('uptimeMonitorManagementApiKeyGenerate')); - expect(setLoadAPIKey).toHaveBeenCalled(); + expect(clickCallback).toHaveBeenCalled(); }); it('shows correct content on loading', () => {