diff --git a/.env b/.env index a225e74138..112d4cd98f 100644 --- a/.env +++ b/.env @@ -31,7 +31,7 @@ HIDE_EXCLUDE_INCLUDE_GIT_COMMITS=true ENABLE_BUILD_CONTEXT=false CLAIR_TOOL_VERSION= ENABLE_RESTART_WORKLOAD=false -ENABLE_SCOPED_VARIABLES=false +ENABLE_SCOPED_VARIABLES=true DEFAULT_CI_TRIGGER_TYPE_MANUAL=false ANNOUNCEMENT_BANNER_MSG= LOGIN_PAGE_IMAGE= diff --git a/.eslintignore b/.eslintignore index 47a0bc5e81..3b9d544b93 100755 --- a/.eslintignore +++ b/.eslintignore @@ -245,13 +245,6 @@ src/components/common/DatePickers/DayPickerRangeController.tsx src/components/common/DeprecatedUpdateWarn.tsx src/components/common/Description/GenericDescription.tsx src/components/common/DynamicTabs/__tests__/DynamicTabs.test.tsx -src/components/common/FloatingVariablesSuggestions/FloatingVariablesSuggestions.tsx -src/components/common/FloatingVariablesSuggestions/SuggestionItem.tsx -src/components/common/FloatingVariablesSuggestions/Suggestions.tsx -src/components/common/FloatingVariablesSuggestions/__tests__/FloatingVariablesSuggestions.test.tsx -src/components/common/FloatingVariablesSuggestions/__tests__/Suggestions.test.tsx -src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsInfo.test.tsx -src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsItem.test.tsx src/components/common/HiddenInput/HiddenInput.tsx src/components/common/List/List.tsx src/components/common/MultiSelect/MultiSelect.tsx diff --git a/package.json b/package.json index 5061bb562e..e7e22bb950 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.5.5", + "@devtron-labs/devtron-fe-common-lib": "1.5.6", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", @@ -30,7 +30,6 @@ "react-csv": "^2.2.2", "react-dates": "^21.8.0", "react-dom": "^17.0.2", - "react-draggable": "^4.4.5", "react-ga4": "^1.4.1", "react-gtm-module": "^2.0.11", "react-mde": "^11.5.0", diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/AppComposeRouter.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/AppComposeRouter.tsx index 1125954eb9..1e3d11eb59 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/AppComposeRouter.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/AppComposeRouter.tsx @@ -16,20 +16,19 @@ import React, { lazy, Suspense } from 'react' import { useRouteMatch, useHistory, Route, Switch, Redirect, useLocation, generatePath } from 'react-router-dom' - import { Progressing, EnvResourceType, BASE_CONFIGURATION_ENV_ID, ApprovalConfigDataKindType, getIsApprovalPolicyConfigured, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' import { ReactComponent as Next } from '@Icons/ic-arrow-forward.svg' import { URLS } from '@Config/index' import { ErrorBoundary, useAppContext } from '@Components/common' import ExternalLinks from '@Components/externalLinks/ExternalLinks' -import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types' import { ConfigMapSecretWrapper } from '@Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper' import { NextButtonProps, STAGE_NAME } from '../AppConfig.types' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigDryRun.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigDryRun.tsx index 8c9897b17d..397af58f70 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigDryRun.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigDryRun.tsx @@ -10,13 +10,13 @@ import { getIsRequestAborted, MODES, useAsync, + ToggleResolveScopedVariables, } from '@devtron-labs/devtron-fe-common-lib' import { importComponentFromFELibrary } from '@Components/common' import { ReactComponent as ICFilePlay } from '@Icons/ic-file-play.svg' // FIXME: Placeholder icon since no sense of git merge icon as of now import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg' import SelectMergeStrategy from './SelectMergeStrategy' -import ToggleResolveScopedVariables from './ToggleResolveScopedVariables' import NoPublishedVersionEmptyState from './NoPublishedVersionEmptyState' import { ConfigDryRunProps } from './types' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigToolbar.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigToolbar.tsx index 2b1ed86e06..0805c8216f 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigToolbar.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ConfigToolbar.tsx @@ -9,6 +9,7 @@ import { BaseURLParams, ComponentSizeType, InvalidYAMLTippyWrapper, + ToggleResolveScopedVariables, } from '@devtron-labs/devtron-fe-common-lib' import { useParams } from 'react-router-dom' import { importComponentFromFELibrary } from '@Components/common' @@ -16,7 +17,6 @@ import { ReactComponent as ICMore } from '@Icons/ic-more-option.svg' import { ReactComponent as ICBookOpen } from '@Icons/ic-book-open.svg' import { ReactComponent as ICInfoOutlineGrey } from '@Icons/ic-info-outline-grey.svg' import BaseConfigurationNavigation from './BaseConfigurationNavigation' -import ToggleResolveScopedVariables from './ToggleResolveScopedVariables' import { PopupMenuItem } from './utils' import { ConfigToolbarProps } from './types' import SelectMergeStrategy from './SelectMergeStrategy' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplate.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplate.tsx index 236a00d66f..a7c5fe0c69 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplate.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/DeploymentTemplate/DeploymentTemplate.tsx @@ -43,10 +43,11 @@ import { getIsRequestAborted, DraftAction, checkIfPathIsMatching, + FloatingVariablesSuggestions, } from '@devtron-labs/devtron-fe-common-lib' import { Prompt, useLocation, useParams } from 'react-router-dom' import YAML from 'yaml' -import { FloatingVariablesSuggestions, importComponentFromFELibrary } from '@Components/common' +import { importComponentFromFELibrary } from '@Components/common' import { getModuleInfo } from '@Components/v2/devtronStackManager/DevtronStackManager.service' import { URLS } from '@Config/routes' import { ReactComponent as ICClose } from '@Icons/ic-close.svg' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx index 312374669c..8fba74597d 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/NoOverrideEmptyState.tsx @@ -4,10 +4,10 @@ import { ComponentSizeType, GenericEmptyState, ImageType, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' import cmCsEmptyState from '@Images/cm-cs-empty-state.png' import { ReactComponent as ICAdd } from '@Icons/ic-add.svg' -import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types' import { NoOverrideEmptyStateProps } from './types' import './NoOverrideEmptyState.scss' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx deleted file mode 100644 index a15a58ac6b..0000000000 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/ToggleResolveScopedVariables.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Toggle, Tooltip } from '@devtron-labs/devtron-fe-common-lib' -import { ReactComponent as ICViewVariableToggle } from '@Icons/ic-view-variable-toggle.svg' -import { ToggleResolveScopedVariablesProps } from './types' - -const ToggleResolveScopedVariables = ({ - resolveScopedVariables, - handleToggleScopedVariablesView, - isDisabled = false, - showTooltip = true, -}: ToggleResolveScopedVariablesProps) => ( - -
- -
-
-) - -export default ToggleResolveScopedVariables diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts index 10d183d482..c0534693ad 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/index.ts @@ -6,7 +6,6 @@ export { default as ConfigHeader } from './ConfigHeader' export { default as NoPublishedVersionEmptyState } from './NoPublishedVersionEmptyState' export { default as CompareConfigView } from './CompareConfigView' export * from './DeploymentConfigCompare' -export { default as ToggleResolveScopedVariables } from './ToggleResolveScopedVariables' export { default as SelectMergeStrategy } from './SelectMergeStrategy' export type { ConfigToolbarProps, CompareConfigViewProps } from './types' diff --git a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts index c568d71d8d..fa503ca1bf 100644 --- a/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts +++ b/src/Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/types.ts @@ -10,8 +10,8 @@ import { ProtectConfigTabsType, SelectPickerOptionType, ServerErrors, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' -import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types' import { FunctionComponent, MutableRefObject, ReactNode } from 'react' import { DeploymentTemplateStateType } from './DeploymentTemplate/types' @@ -219,16 +219,6 @@ export type ConfigDryRunProps = { } & ConfigDryRunManifestProps & ConfigErrorHandlingProps -export interface ToggleResolveScopedVariablesProps { - resolveScopedVariables: boolean - handleToggleScopedVariablesView: () => void - isDisabled?: boolean - /** - * @default true - */ - showTooltip?: boolean -} - export enum DeploymentTemplateComponentType { DEPLOYMENT_TEMPLATE = '3', } diff --git a/src/Pages/GlobalConfigurations/BuildInfra/BuildInfra.tsx b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfra.tsx index 5e3c90539d..8f5f274b1f 100644 --- a/src/Pages/GlobalConfigurations/BuildInfra/BuildInfra.tsx +++ b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfra.tsx @@ -19,6 +19,7 @@ import { ErrorScreenNotAuthorized } from '@devtron-labs/devtron-fe-common-lib' import { importComponentFromFELibrary } from '../../../components/common' import { BuildInfraProps } from './types' import ProfileForm from './ProfileForm' +import BuildInfraUtilityProvider from './BuildInfraUtiltityProvider' const BuildInfraRouter = importComponentFromFELibrary('BuildInfraRouter', null, 'function') @@ -28,7 +29,11 @@ export const BuildInfra: FunctionComponent = ({ isSuperAdmin }) } if (BuildInfraRouter) { - return + return ( + + + + ) } return diff --git a/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraCMCSForm.tsx b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraCMCSForm.tsx new file mode 100644 index 0000000000..ad4cacd016 --- /dev/null +++ b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraCMCSForm.tsx @@ -0,0 +1,22 @@ +import { BuildInfraCMCSFormProps } from '@devtron-labs/devtron-fe-common-lib' +import { ConfigMapSecretForm } from '@Pages/Shared/ConfigMapSecret/ConfigMapSecretForm' + +const BuildInfraCMCSForm = ({ parsedData, useFormProps, componentType }: BuildInfraCMCSFormProps) => ( + +) + +export default BuildInfraCMCSForm diff --git a/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraUtiltityProvider.tsx b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraUtiltityProvider.tsx new file mode 100644 index 0000000000..9248336a32 --- /dev/null +++ b/src/Pages/GlobalConfigurations/BuildInfra/BuildInfraUtiltityProvider.tsx @@ -0,0 +1,17 @@ +import { useMemo } from 'react' +import { BuildInfraUtilityContext, BuildInfraUtilityContextType } from '@devtron-labs/devtron-fe-common-lib' +import BuildInfraCMCSForm from './BuildInfraCMCSForm' +import { BuildInfraUtilityProviderProps } from './types' + +const BuildInfraUtilityProvider = ({ children }: BuildInfraUtilityProviderProps) => { + const value: BuildInfraUtilityContextType = useMemo( + () => ({ + BuildInfraCMCSForm, + }), + [], + ) + + return {children} +} + +export default BuildInfraUtilityProvider diff --git a/src/Pages/GlobalConfigurations/BuildInfra/types.tsx b/src/Pages/GlobalConfigurations/BuildInfra/types.tsx index fa82d80769..da3ab4ae35 100644 --- a/src/Pages/GlobalConfigurations/BuildInfra/types.tsx +++ b/src/Pages/GlobalConfigurations/BuildInfra/types.tsx @@ -14,6 +14,12 @@ * limitations under the License. */ +import { ReactNode } from 'react' + export interface BuildInfraProps { isSuperAdmin: boolean } + +export interface BuildInfraUtilityProviderProps { + children: ReactNode +} diff --git a/src/Pages/Releases/Detail/Configurations/Configurations.tsx b/src/Pages/Releases/Detail/Configurations/Configurations.tsx index 1b1b253dd2..dd13bbce71 100644 --- a/src/Pages/Releases/Detail/Configurations/Configurations.tsx +++ b/src/Pages/Releases/Detail/Configurations/Configurations.tsx @@ -9,11 +9,12 @@ import { getIsApprovalPolicyConfigured, Progressing, useAsync, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' import { URLS } from '@Config/routes' import { importComponentFromFELibrary } from '@Components/common' -import { ConfigMapSecretWrapper, CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret' +import { ConfigMapSecretWrapper } from '@Pages/Shared/ConfigMapSecret' import { DeploymentConfigCompare, DeploymentTemplate } from '@Pages/Applications' import { getEnvConfig } from '@Pages/Applications/DevtronApps/service' import { EnvConfigurationsNav } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvConfigurationsNav' diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts index 23422eb475..91bd6c2c84 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.service.ts @@ -19,7 +19,6 @@ import { get, post, trash, - ResponseType, getResolvedDeploymentTemplate, ValuesAndManifestFlagDTO, GetResolvedDeploymentTemplateProps, @@ -29,14 +28,14 @@ import { getIsRequestAborted, DraftMetadataDTO, showError, - ConfigMapSecretDataDTO, + JobCMSecretDataDTO, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' import { Routes } from '@Config/constants' import { importComponentFromFELibrary } from '@Components/common' import { - CMSecretComponentType, GetConfigMapSecretConfigDataProps, GetConfigMapSecretConfigDataReturnType, UpdateConfigMapSecretProps, @@ -106,14 +105,14 @@ export const overRideSecret = ({ appId, envId, payload, signal }: OverrideConfig { signal }, ) -export const getCMSecret = ({ +export const getJobCMSecret = ({ componentType, id, appId, envId, name, signal, -}: GetCMSecretProps): Promise>> => { +}: GetCMSecretProps): Promise => { let url = '' if (envId !== null && envId !== undefined) { url = `${ @@ -140,7 +139,7 @@ export const getConfigMapSecretConfigData = async ) => { try { const { result } = await (isJob - ? getCMSecret({ + ? getJobCMSecret({ componentType, id: resourceId, appId, diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx index 54d5068cc9..36e3304d6a 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper.tsx @@ -8,13 +8,14 @@ import { Progressing, showError, useAsync, + CMSecretComponentType, } from '@devtron-labs/devtron-fe-common-lib' import { getAppChartRefForAppAndEnv } from '@Services/service' import { ComponentStates } from '@Pages/Shared/EnvironmentOverride/EnvironmentOverrides.types' import { CM_SECRET_COMPONENT_NAME } from './constants' -import { CMSecretComponentType, CMSecretWrapperProps } from './types' +import { CMSecretWrapperProps } from './types' import { ConfigMapSecretContainer } from './ConfigMapSecretContainer' diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx index 0d98168717..a1f3228098 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretContainer.tsx @@ -26,6 +26,15 @@ import { usePrompt, checkIfPathIsMatching, useUrlFilters, + ConfigMapSecretUseFormProps, + CMSecretComponentType, + CM_SECRET_STATE, + getConfigMapSecretFormInitialValues, + getConfigMapSecretPayload, + CMSecretPayloadType, + getConfigMapSecretFormValidations, + ConfigMapSecretReadyOnly, + FloatingVariablesSuggestions, UseFormErrorHandler, UseFormSubmitHandler, } from '@devtron-labs/devtron-fe-common-lib' @@ -33,9 +42,10 @@ import { import { URLS } from '@Config/routes' import { ConfigHeader, ConfigToolbar, ConfigToolbarProps, NoOverrideEmptyState } from '@Pages/Applications' import { getConfigToolbarPopupConfig } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/utils' -import { FloatingVariablesSuggestions, importComponentFromFELibrary } from '@Components/common' +import { importComponentFromFELibrary } from '@Components/common' import { EnvConfigObjectKey } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/AppConfig.types' +import { DEFAULT_MERGE_STRATEGY } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants' import { getConfigMapSecretConfigData, getConfigMapSecretConfigDraftData, @@ -48,31 +58,23 @@ import { import { getConfigMapSecretDraftAndPublishedData, getConfigMapSecretError, - getConfigMapSecretFormInitialValues, getConfigMapSecretInheritedData, - getConfigMapSecretPayload, getConfigMapSecretResolvedData, getConfigMapSecretResolvedDataPayload, getConfigMapSecretStateLabel, parseConfigMapSecretSearchParams, } from './utils' -import { getConfigMapSecretFormValidations } from './validations' import { CM_SECRET_COMPONENT_NAME, CONFIG_MAP_SECRET_NO_DATA_ERROR } from './constants' import { - CM_SECRET_STATE, - CMSecretComponentType, CMSecretDeleteModalType, CMSecretDraftPayloadType, - CMSecretPayloadType, ConfigMapSecretContainerProps, ConfigMapSecretFormProps, ConfigMapSecretQueryParamsType, - ConfigMapSecretUseFormProps, } from './types' import { ConfigMapSecretDeleteModal } from './ConfigMapSecretDeleteModal' import { ConfigMapSecretForm } from './ConfigMapSecretForm' -import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly' import { ConfigMapSecretProtected } from './ConfigMapSecretProtected' import { ConfigMapSecretNullState } from './ConfigMapSecretNullState' import { ConfigMapSecretDryRun } from './ConfigMapSecretDryRun' @@ -135,8 +137,10 @@ export const ConfigMapSecretContainer = ({ componentType, cmSecretStateLabel: envId ? CM_SECRET_STATE.ENV : CM_SECRET_STATE.BASE, isJob, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }), }) + const { data: formData, errors: formErrors, formState, setValue, handleSubmit, reset } = useFormProps // CONSTANTS @@ -356,6 +360,7 @@ export const ConfigMapSecretContainer = ({ cmSecretStateLabel, skipValidation: !isCreateState && !formInitialData, isJob, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }) }, [configMapSecretData, draftData]) @@ -615,6 +620,7 @@ export const ConfigMapSecretContainer = ({ cmSecretStateLabel, componentType, configMapSecretData: { ...configMapSecretData, mergeStrategy: OverrideMergeStrategyType.REPLACE }, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }) setValue('yaml', yaml) setValue('currentData', currentData) @@ -627,6 +633,7 @@ export const ConfigMapSecretContainer = ({ cmSecretStateLabel, componentType, configMapSecretData: inheritedConfigMapSecretData, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }) : formInitialValues), mergeStrategy: strategy, @@ -831,7 +838,7 @@ export const ConfigMapSecretContainer = ({ /> ) : ( ) case ConfigHeaderTabType.DRY_RUN: diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx index f90bcc909f..8a389fdcbd 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx @@ -14,24 +14,26 @@ import { ToastManager, ToastVariantType, YAMLStringify, + convertKeyValuePairToYAML, + configMapSecretMountDataMap, + convertYAMLToKeyValuePair, + CODE_EDITOR_RADIO_STATE, + PATTERNS, } from '@devtron-labs/devtron-fe-common-lib' import { ReactComponent as ICPencil } from '@Icons/ic-pencil.svg' import { ReactComponent as HideIcon } from '@Icons/ic-visibility-off.svg' import { ReactComponent as ICErrorExclamation } from '@Icons/ic-error-exclamation.svg' -import { PATTERNS } from '@Config/constants' import { - CODE_EDITOR_RADIO_STATE, CODE_EDITOR_RADIO_STATE_VALUE, CONFIG_MAP_SECRET_NO_DATA_ERROR, - configMapSecretMountDataMap, DATA_HEADER_MAP, sampleJSONs, VIEW_MODE, } from './constants' import { externalTypeSecretCodeEditorDataHeaders, renderYamlInfoText } from './helpers' -import { convertKeyValuePairToYAML, convertYAMLToKeyValuePair, getLockedYamlString } from './utils' +import { getLockedYamlString } from './utils' import { ConfigMapSecretDataProps } from './types' export const ConfigMapSecretData = ({ diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx index 1bb59b9889..9610ad8dd2 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDeleteModal.tsx @@ -6,13 +6,15 @@ import { showError, ToastManager, ToastVariantType, + CMSecretComponentType, + CM_SECRET_STATE, } from '@devtron-labs/devtron-fe-common-lib' import { importComponentFromFELibrary } from '@Components/common' import { deleteEnvSecret, deleteEnvConfigMap, deleteSecret, deleteConfigMap } from './ConfigMapSecret.service' import { CM_SECRET_COMPONENT_NAME } from './constants' -import { CM_SECRET_STATE, CMSecretComponentType, ConfigMapSecretDeleteModalProps } from './types' +import { ConfigMapSecretDeleteModalProps } from './types' const DeleteModal = importComponentFromFELibrary('DeleteModal') const DeleteOverrideDraftModal = importComponentFromFELibrary('DeleteOverrideDraftModal') diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDryRun.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDryRun.tsx index f907244270..ea9900b99f 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDryRun.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretDryRun.tsx @@ -17,19 +17,22 @@ import { useAsync, checkIfPathIsMatching, usePrompt, + CM_SECRET_STATE, + ConfigMapSecretReadyOnly, + ToggleResolveScopedVariables, } from '@devtron-labs/devtron-fe-common-lib' import { ReactComponent as ICFilePlay } from '@Icons/ic-file-play.svg' import { ReactComponent as ICFileCode } from '@Icons/ic-file-code.svg' import { importComponentFromFELibrary } from '@Components/common' -import { NoPublishedVersionEmptyState, SelectMergeStrategy, ToggleResolveScopedVariables } from '@Pages/Applications' +import { NoPublishedVersionEmptyState, SelectMergeStrategy } from '@Pages/Applications' +import { DEFAULT_MERGE_STRATEGY } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants' import { getConfigMapSecretManifest } from './ConfigMapSecret.service' -import { CM_SECRET_STATE, ConfigMapSecretDryRunProps } from './types' +import { ConfigMapSecretDryRunProps } from './types' import { renderExternalInfo } from './helpers' import { getDryRunConfigMapSecretData } from './utils' -import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly' import { ConfigMapSecretNullState } from './ConfigMapSecretNullState' const DryRunEditorModeSelect = importComponentFromFELibrary('DryRunEditorModeSelect', null, 'function') @@ -174,6 +177,7 @@ export const ConfigMapSecretDryRun = ({ cmSecretStateLabel={CM_SECRET_STATE.BASE} configMapSecretData={{ ...dryRunConfigMapSecretData, mergeStrategy: null }} areScopeVariablesResolving={areScopeVariablesResolving} + fallbackMergeStrategy={DEFAULT_MERGE_STRATEGY} />
{renderExternalInfo( diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx index defe8ab244..6d16552950 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretForm.tsx @@ -19,6 +19,13 @@ import { stopPropagation, usePrompt, checkIfPathIsMatching, + CM_SECRET_STATE, + configMapSecretMountDataMap, + configMapDataTypeOptions, + ConfigMapSecretDataTypeOptionType, + renderHashiOrAwsDeprecatedInfo, + getSecretDataTypeOptions, + ConfigMapSecretReadyOnly, } from '@devtron-labs/devtron-fe-common-lib' import { ROLLOUT_DEPLOYMENT } from '@Config/constants' @@ -28,21 +35,11 @@ import { isVersionLessThanOrEqualToTarget, } from '@Components/common' -import { - CM_SECRET_COMPONENT_NAME, - configMapDataTypeOptions, - configMapSecretMountDataMap, - getSecretDataTypeOptions, -} from './constants' -import { - renderChartVersionBelow3090NotSupportedText, - renderESOInfo, - renderExternalInfo, - renderHashiOrAwsDeprecatedInfo, -} from './helpers' -import { ConfigMapSecretFormProps, ConfigMapSecretDataTypeOptionType, CM_SECRET_STATE } from './types' +import { DEFAULT_MERGE_STRATEGY } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants' +import { CM_SECRET_COMPONENT_NAME } from './constants' +import { renderChartVersionBelow3090NotSupportedText, renderESOInfo, renderExternalInfo } from './helpers' +import { ConfigMapSecretFormProps } from './types' import { ConfigMapSecretData } from './ConfigMapSecretData' -import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly' const DISABLE_DATA_TYPE_CHANGE_HELPER_MESSAGE = importComponentFromFELibrary( 'DISABLE_DATA_TYPE_CHANGE_HELPER_MESSAGE', @@ -51,7 +48,7 @@ const DISABLE_DATA_TYPE_CHANGE_HELPER_MESSAGE = importComponentFromFELibrary( ) export const ConfigMapSecretForm = ({ - id = null, + isCreateView = false, configMapSecretData, inheritedConfigMapSecretData, cmSecretStateLabel, @@ -60,12 +57,14 @@ export const ConfigMapSecretForm = ({ isDraft, disableDataTypeChange, componentType, - isSubmitting, + isSubmitting = false, isApprovalPolicyConfigured, areScopeVariablesResolving, useFormProps, + isExternalSubmit, onSubmit, onCancel, + noContainerPadding = false, }: ConfigMapSecretFormProps) => { // HOOKS const location = useLocation() @@ -74,7 +73,6 @@ export const ConfigMapSecretForm = ({ const { data, errors, formState, setValue, register } = useFormProps // CONSTANTS - const isCreateView = id === null const componentName = CM_SECRET_COMPONENT_NAME[componentType] const isUnAuthorized = configMapSecretData?.unAuthorized const isESO = data.isSecret && hasESO(data.externalType) @@ -91,7 +89,7 @@ export const ConfigMapSecretForm = ({ * * This ensures the user is warned about losing data when navigating away during creation. * * Non-create mode is being handled by the parent component. */ - const shouldPrompt = isCreateView && formState.isDirty + const shouldPrompt = !isExternalSubmit && isCreateView && formState.isDirty // PROMPT FOR UNSAVED CHANGES usePrompt({ shouldPrompt }) @@ -353,7 +351,7 @@ export const ConfigMapSecretForm = ({ {areScopeVariablesResolving ? ( ) : ( -
+
{isPatchMode ? ( ) : ( <> @@ -389,7 +388,7 @@ export const ConfigMapSecretForm = ({
)} - {!isHashiOrAWS && renderFormButtons()} + {!isHashiOrAWS && !isExternalSubmit && renderFormButtons()} ) diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx index 2285e79c98..24c3c4490f 100644 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx +++ b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretProtected.tsx @@ -11,15 +11,19 @@ import { Progressing, ProtectConfigTabsType, SelectPickerOptionType, + CM_SECRET_STATE, + CMSecretConfigData, + getConfigMapSecretPayload, + getConfigMapSecretReadOnlyValues, + ConfigMapSecretReadyOnly, } from '@devtron-labs/devtron-fe-common-lib' import { CompareConfigView, CompareConfigViewProps, NoPublishedVersionEmptyState } from '@Pages/Applications' import { importComponentFromFELibrary } from '@Components/common' -import { CM_SECRET_STATE, CMSecretConfigData, ConfigMapSecretProtectedProps } from './types' -import { getConfigMapSecretPayload, getConfigMapSecretReadOnlyValues } from './utils' +import { DEFAULT_MERGE_STRATEGY } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants' +import { ConfigMapSecretProtectedProps } from './types' import { ConfigMapSecretForm } from './ConfigMapSecretForm' -import { ConfigMapSecretReadyOnly } from './ConfigMapSecretReadyOnly' import { ConfigMapSecretNullState } from './ConfigMapSecretNullState' const ConfigMapSecretApproveButton = importComponentFromFELibrary('ConfigMapSecretApproveButton', null, 'function') @@ -119,6 +123,7 @@ export const ConfigMapSecretProtected = ({ componentType, configMapSecretData: diffViewData, cmSecretStateLabel, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }) return { @@ -170,6 +175,7 @@ export const ConfigMapSecretProtected = ({ } : null, isJob, + fallbackMergeStrategy: DEFAULT_MERGE_STRATEGY, }) return { @@ -205,7 +211,7 @@ export const ConfigMapSecretProtected = ({ ) case ProtectConfigTabsType.EDIT_DRAFT: diff --git a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx b/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx deleted file mode 100644 index 5184055ce7..0000000000 --- a/src/Pages/Shared/ConfigMapSecret/ConfigMapSecretReadyOnly.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { ClipboardButton, CodeEditor, hasHashiOrAWS, Progressing } from '@devtron-labs/devtron-fe-common-lib' - -import { getConfigMapSecretReadOnlyValues } from './utils' -import { ConfigMapSecretReadyOnlyProps } from './types' -import { renderHashiOrAwsDeprecatedInfo } from './helpers' - -export const ConfigMapSecretReadyOnly = ({ - componentType, - isJob, - configMapSecretData, - cmSecretStateLabel, - areScopeVariablesResolving, - hideCodeEditor = false, -}: ConfigMapSecretReadyOnlyProps) => { - const displayValues = getConfigMapSecretReadOnlyValues({ - configMapSecretData, - cmSecretStateLabel, - componentType, - isJob, - }) - - return areScopeVariablesResolving ? ( - - ) : ( -
- {hasHashiOrAWS(configMapSecretData?.externalType) && renderHashiOrAwsDeprecatedInfo()} -
- {displayValues.configData.map(({ displayName, value }) => - value ? ( -
-

{displayName}

-

{value}

-
- ) : null, - )} -
- {!hideCodeEditor && displayValues.data && ( -
-
-

Data

- -
-
- -
-
- )} -
- ) -} diff --git a/src/Pages/Shared/ConfigMapSecret/constants.ts b/src/Pages/Shared/ConfigMapSecret/constants.ts index 38b32ae40b..21af97733f 100644 --- a/src/Pages/Shared/ConfigMapSecret/constants.ts +++ b/src/Pages/Shared/ConfigMapSecret/constants.ts @@ -1,13 +1,5 @@ -import { GroupBase, OptionsOrGroups } from 'react-select' - -import { CMSecretExternalType } from '@devtron-labs/devtron-fe-common-lib' - -import { - CMSecretComponentType, - CMSecretYamlData, - ConfigMapSecretDataTypeOptionType, - ConfigMapSecretNullStateProps, -} from './types' +import { CMSecretComponentType } from '@devtron-labs/devtron-fe-common-lib' +import { ConfigMapSecretNullStateProps } from './types' export const CM_SECRET_COMPONENT_NAME = { [CMSecretComponentType.ConfigMap]: 'ConfigMap', @@ -64,72 +56,6 @@ export const getCMSecretNullStateText = ( }, }) -export const configMapDataTypeOptions: ConfigMapSecretDataTypeOptionType[] = [ - { value: '', label: 'Kubernetes ConfigMap' }, - { value: CMSecretExternalType.KubernetesConfigMap, label: 'Kubernetes External ConfigMap' }, -] - -export const getSecretDataTypeOptions = ( - isJob: boolean, - isHashiOrAWS: boolean, -): - | ConfigMapSecretDataTypeOptionType[] - | OptionsOrGroups> => { - const kubernetesOptions: ConfigMapSecretDataTypeOptionType[] = [ - { value: '', label: 'Kubernetes Secret' }, - { value: CMSecretExternalType.KubernetesSecret, label: 'Mount Existing Kubernetes Secret' }, - ] - - const esoOptions: GroupBase[] = [ - { - label: 'External Secret Operator (ESO)', - options: [ - { value: CMSecretExternalType.ESO_GoogleSecretsManager, label: 'Google Secrets Manager' }, - { value: CMSecretExternalType.ESO_AWSSecretsManager, label: 'AWS Secrets Manager' }, - { value: CMSecretExternalType.ESO_AzureSecretsManager, label: 'Azure Secrets Manager' }, - { value: CMSecretExternalType.ESO_HashiCorpVault, label: 'Hashi Corp Vault' }, - ], - }, - ] - - const kesOptions: GroupBase[] = [ - { - label: 'Kubernetes External Secret (KES)', - options: [ - { - value: CMSecretExternalType.AWSSecretsManager, - label: 'AWS Secrets Manager', - description: 'Deprecated', - }, - { - value: CMSecretExternalType.AWSSystemManager, - label: 'AWS System Manager', - description: 'Deprecated', - }, - { - value: CMSecretExternalType.HashiCorpVault, - label: 'Hashi Corp Vault', - description: 'Deprecated', - }, - ], - }, - ] - - return isJob ? kubernetesOptions : [...kubernetesOptions, ...esoOptions, ...(isHashiOrAWS ? kesOptions : [])] -} - -export const configMapSecretMountDataMap = { - environment: { title: 'Environment Variable', value: 'environment' }, - volume: { title: 'Data Volume', value: 'volume' }, -} - -export const CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA: CMSecretYamlData[] = [{ k: '', v: '', id: 0 }] - -export enum CODE_EDITOR_RADIO_STATE { - DATA = 'data', - SAMPLE = 'sample', -} - export const CODE_EDITOR_RADIO_STATE_VALUE = { DATA: 'Data', SAMPLE: 'Sample' } export const DATA_HEADER_MAP = { DEFAULT: 'default' } @@ -270,14 +196,3 @@ export const sampleJSONs = { } export const CONFIG_MAP_SECRET_NO_DATA_ERROR = 'This is a required field' - -export const CONFIG_MAP_SECRET_YAML_PARSE_ERROR = 'Could not parse to valid YAML' - -export const SECRET_TOAST_INFO = { - BOTH_STORE_AVAILABLE: 'Please use either secretStore or secretStoreRef', - CHECK_KEY_SECRET_KEY: 'Please check key and secretKey', - BOTH_STORE_UNAVAILABLE: 'Please provide secretStore or secretStoreRef', - CHECK_KEY_NAME: 'Please check key and name', - BOTH_ESO_DATA_AND_DATA_FROM_AVAILABLE: 'Please use either esoData or esoDataFrom', - BOTH_ESO_DATA_AND_DATA_FROM_UNAVAILABLE: 'Please provide esoData or esoDataFrom', -} diff --git a/src/Pages/Shared/ConfigMapSecret/helpers.tsx b/src/Pages/Shared/ConfigMapSecret/helpers.tsx index df5c71ab00..c430e2409e 100644 --- a/src/Pages/Shared/ConfigMapSecret/helpers.tsx +++ b/src/Pages/Shared/ConfigMapSecret/helpers.tsx @@ -1,15 +1,13 @@ import { Link } from 'react-router-dom' -import { CMSecretExternalType, InfoColourBar } from '@devtron-labs/devtron-fe-common-lib' +import { CMSecretExternalType, InfoColourBar, CMSecretComponentType } from '@devtron-labs/devtron-fe-common-lib' import { ReactComponent as InfoIcon } from '@Icons/info-filled.svg' import { ReactComponent as InfoIconN7 } from '@Icons/info-filled-n7.svg' -import { ReactComponent as ICWarningY5 } from '@Icons/ic-warning-y5.svg' import { URLS } from '@Config/routes' import { DOCUMENTATION } from '@Config/constants' import { EXTERNAL_INFO_TEXT } from './constants' -import { CMSecretComponentType } from './types' export const renderESOInfo = (isESO: boolean) => isESO ? ( @@ -60,31 +58,6 @@ export const renderExternalInfo = ( /> ) : null -export const renderHashiOrAwsDeprecatedInfo = () => ( - - - Kubernetes External Secret (KES) has been deprecated and will be removed in the next Devtron - version. You can delete this file and create a secret using - -   - - External Secret Operator (ESO). - -

- } - Icon={ICWarningY5} - iconSize={20} - /> -) - export const renderChartVersionBelow3090NotSupportedText = () => ( Supported for Chart Versions 3.10 and above.  diff --git a/src/Pages/Shared/ConfigMapSecret/index.ts b/src/Pages/Shared/ConfigMapSecret/index.ts index b0d37367f1..6a705aa869 100644 --- a/src/Pages/Shared/ConfigMapSecret/index.ts +++ b/src/Pages/Shared/ConfigMapSecret/index.ts @@ -1,4 +1,3 @@ export * from './ConfigMapSecret.wrapper' export type { CMSecretWrapperProps } from './types' -export { CMSecretComponentType } from './types' diff --git a/src/Pages/Shared/ConfigMapSecret/types.ts b/src/Pages/Shared/ConfigMapSecret/types.ts index 2bd99d12c5..3ad9b2d161 100644 --- a/src/Pages/Shared/ConfigMapSecret/types.ts +++ b/src/Pages/Shared/ConfigMapSecret/types.ts @@ -1,64 +1,27 @@ import { Dispatch, MutableRefObject, SetStateAction } from 'react' import { - ConfigDatum, - CMSecretExternalType, + CMSecretConfigData, + ConfigMapSecretUseFormProps, DraftAction, DraftMetadataDTO, ProtectConfigTabsType, - SelectPickerOptionType, useForm, AppEnvDeploymentConfigDTO, DryRunEditorMode, ConfigHeaderTabType, OverrideMergeStrategyType, ConfigMapSecretDataType, + CMSecretComponentType, + CM_SECRET_STATE, + CMSecretPayloadType, } from '@devtron-labs/devtron-fe-common-lib' import { ConfigToolbarProps } from '@Pages/Applications' import { ComponentStates, EnvironmentOverrideComponentProps } from '../EnvironmentOverride/EnvironmentOverrides.types' -// ENUMS -export enum CMSecretComponentType { - ConfigMap = 1, - Secret = 2, -} - -export enum CM_SECRET_STATE { - BASE = '', - INHERITED = 'INHERITING', - OVERRIDDEN = 'OVERRIDDEN', - ENV = 'ENV', - UNPUBLISHED = 'UNPUBLISHED', -} - // PAYLOAD PROPS -export type CMSecretPayloadType = Pick< - CMSecretConfigData, - | 'data' - | 'name' - | 'type' - | 'externalType' - | 'external' - | 'roleARN' - | 'mountPath' - | 'subPath' - | 'esoSecretData' - | 'filePermission' - | 'esoSubPath' - | 'mergeStrategy' -> - -export interface ESOSecretData { - secretStore: Record - secretStoreRef: Record - refreshInterval: string - esoData: Record[] - esoDataFrom: Record[] - template: Record -} - export interface CMSecretDraftPayloadType { id: number appId: number @@ -80,39 +43,6 @@ export type GetConfigMapSecretConfigDataReturnType = IsJo ? ConfigMapSecretDataType : AppEnvDeploymentConfigDTO -// SELECT PICKER OPTION TYPE -export type ConfigMapSecretDataTypeOptionType = SelectPickerOptionType - -// USE FORM PROPS -export interface CMSecretYamlData { - k: string - v: string - id: string | number -} - -export interface ConfigMapSecretUseFormProps { - name: string - isSecret: boolean - external: boolean - externalType: CMSecretExternalType - selectedType: string - isFilePermissionChecked: boolean - isSubPathChecked: boolean - externalSubpathValues: string - filePermission: string - volumeMountPath: string - roleARN: string - yamlMode: boolean - yaml: string - currentData: CMSecretYamlData[] - secretDataYaml: string - esoSecretYaml: string - hasCurrentDataErr: boolean - isResolvedData: boolean - mergeStrategy: ConfigToolbarProps['mergeStrategy'] - skipValidation: boolean -} - // COMPONENT PROPS export interface CMSecretDraftData extends Omit { unAuthorized: boolean @@ -138,26 +68,43 @@ export interface ConfigMapSecretContainerProps extends Omit void + onCancel: () => void + } -export interface ConfigMapSecretFormProps - extends Required< - Pick - > { - id: number - configMapSecretData: CMSecretConfigData - inheritedConfigMapSecretData: CMSecretConfigData - cmSecretStateLabel: CM_SECRET_STATE - isSubmitting: boolean - areScopeVariablesResolving: boolean - isDraft?: boolean - disableDataTypeChange: boolean - onSubmit: () => void - onCancel: () => void - useFormProps: ReturnType> -} +export type ConfigMapSecretFormProps = Required< + Pick +> & + CMCSFormBaseProps & { + /** + * @default false + */ + isCreateView?: boolean + configMapSecretData: CMSecretConfigData + inheritedConfigMapSecretData: CMSecretConfigData + cmSecretStateLabel: CM_SECRET_STATE + isSubmitting?: boolean + areScopeVariablesResolving: boolean + isDraft?: boolean + disableDataTypeChange: boolean + useFormProps: ReturnType> + /** + * @default false + */ + noContainerPadding?: boolean + /** + * This is also being used in BuildInfra + */ + isJob?: boolean + } export interface ConfigMapSecretDataProps extends Pick { isESO: boolean @@ -167,20 +114,13 @@ export interface ConfigMapSecretDataProps extends Pick { - hideCodeEditor?: boolean -} - export type CMSecretDeleteModalType = 'deleteModal' | 'protectedDeleteModal' export interface ConfigMapSecretDeleteModalProps - extends Pick { + extends Pick { appId: number envId: number + id: number configName: string openDeleteModal: CMSecretDeleteModalType draftData: CMSecretDraftData @@ -213,13 +153,13 @@ export type ConfigMapSecretProtectedProps = Pick & Pick & { + id: number componentName: string publishedConfigMapSecretData: ConfigMapSecretFormProps['configMapSecretData'] inheritedConfigMapSecretData: ConfigMapSecretFormProps['configMapSecretData'] diff --git a/src/Pages/Shared/ConfigMapSecret/utils.ts b/src/Pages/Shared/ConfigMapSecret/utils.ts index 3ef1ed1be5..de83257a97 100644 --- a/src/Pages/Shared/ConfigMapSecret/utils.ts +++ b/src/Pages/Shared/ConfigMapSecret/utils.ts @@ -3,7 +3,6 @@ import YAML from 'yaml' import { AppEnvDeploymentConfigDTO, applyCompareDiffOnUneditedDocument, - CMSecretExternalType, ConfigHeaderTabType, ConfigMapSecretDataType, decode, @@ -13,33 +12,19 @@ import { DraftState, DryRunEditorMode, ERROR_STATUS_CODE, - getSelectPickerOptionByValue, - hasESO, OverrideMergeStrategyType, YAMLStringify, - noop, + ConfigMapSecretUseFormProps, + CM_SECRET_STATE, + CMSecretConfigData, + getConfigMapSecretPayload, } from '@devtron-labs/devtron-fe-common-lib' import { ResourceConfigStage } from '@Pages/Applications/DevtronApps/service.types' -import { DEFAULT_MERGE_STRATEGY } from '@Pages/Applications/DevtronApps/Details/AppConfigurations/MainContent/constants' -import { - CODE_EDITOR_RADIO_STATE, - CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA, - configMapDataTypeOptions, - configMapSecretMountDataMap, - getSecretDataTypeOptions, -} from './constants' import { - CMSecretYamlData, ConfigMapSecretFormProps, - ConfigMapSecretUseFormProps, - CMSecretComponentType, - CM_SECRET_STATE, CMSecretDraftData, - CMSecretPayloadType, - CMSecretConfigData, - ESOSecretData, ConfigMapSecretDecodedDataReturnType, ConfigMapSecretDecodedDataProps, ConfigMapSecretEncodedDataProps, @@ -68,88 +53,6 @@ export const getConfigMapSecretStateLabel = (configStage: ResourceConfigStage, i // HELPERS UTILS ---------------------------------------------------------------- // FORM UTILS ---------------------------------------------------------------- -const secureValues = (data: Record, decodeData: boolean): CMSecretYamlData[] => { - let decodedData = data || DEFAULT_SECRET_PLACEHOLDER - - if (decodeData) { - try { - decodedData = decode(data) - } catch { - noop() - } - } - - return Object.keys(decodedData).map((k, id) => ({ - k, - v: typeof decodedData[k] === 'object' ? YAMLStringify(decodedData[k]) : decodedData[k], - id, - })) -} - -const processCurrentData = ({ - configMapSecretData, - cmSecretStateLabel, - isSecret, -}: Pick & { - isSecret: boolean -}) => { - if (configMapSecretData.mergeStrategy === OverrideMergeStrategyType.PATCH) { - if (configMapSecretData.patchData) { - return secureValues(configMapSecretData.patchData, isSecret && configMapSecretData.externalType === '') - } - - return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA - } - - if (configMapSecretData.data) { - return secureValues(configMapSecretData.data, isSecret && configMapSecretData.externalType === '') - } - - if (cmSecretStateLabel === CM_SECRET_STATE.INHERITED && configMapSecretData.defaultData) { - return secureValues(configMapSecretData.defaultData, isSecret && configMapSecretData.externalType === '') - } - - return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA -} - -const processExternalSubPathValues = ({ - data, - esoSubPath, - external, - subPath, -}: Pick) => { - if (subPath && external && data) { - return Object.keys(data).join(', ') - } - if (esoSubPath) { - return esoSubPath.join(', ') - } - return '' -} - -export const convertYAMLToKeyValuePair = (yaml: string): CMSecretYamlData[] => { - try { - const obj = yaml && YAML.parse(yaml) - if (typeof obj !== 'object') { - throw new Error() - } - const keyValueArray: CMSecretYamlData[] = Object.keys(obj).reduce((agg, k, id) => { - if (!k && !obj[k]) { - return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA - } - const v = obj[k] && typeof obj[k] === 'object' ? YAMLStringify(obj[k]) : obj[k].toString() - - return [...agg, { k, v: v ?? '', id }] - }, []) - return keyValueArray - } catch { - return CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA - } -} - -export const convertKeyValuePairToYAML = (currentData: CMSecretYamlData[]) => - currentData.length ? YAMLStringify(currentData.reduce((agg, { k, v }) => ({ ...agg, [k]: v }), {})) : '' - export const getLockedYamlString = (yaml: string) => { const obj = YAML.parse(yaml) const keyValueArray = Object.keys(obj).reduce((agg, k) => { @@ -174,354 +77,9 @@ export const getYAMLWithStringifiedNumbers = (yaml: string) => { return YAMLStringify(jsonWithStringifiedNumbers) } -const getSecretDataFromConfigData = ({ - secretData, - defaultSecretData, - esoSecretData: baseEsoSecretData, - defaultESOSecretData, -}: ConfigMapSecretFormProps['configMapSecretData']): Pick< - ConfigMapSecretUseFormProps, - 'secretDataYaml' | 'esoSecretYaml' -> => { - let jsonForSecretDataYaml: string - - if (secretData?.length) { - jsonForSecretDataYaml = YAMLStringify(secretData) - } else if (defaultSecretData?.length) { - jsonForSecretDataYaml = YAMLStringify(defaultSecretData) - } - - const esoSecretData: Record = - !(baseEsoSecretData?.esoData || []).length && - !baseEsoSecretData?.template && - !baseEsoSecretData?.esoDataFrom && - defaultESOSecretData - ? defaultESOSecretData - : baseEsoSecretData - - const isEsoSecretData: boolean = - (esoSecretData?.secretStore || esoSecretData?.secretStoreRef) && - (esoSecretData.esoData || esoSecretData.template || esoSecretData.esoDataFrom) - - return { - secretDataYaml: jsonForSecretDataYaml ?? '', - esoSecretYaml: isEsoSecretData ? YAMLStringify(esoSecretData) : '', - } -} - -export const getConfigMapSecretFormInitialValues = ({ - isJob, - configMapSecretData, - cmSecretStateLabel, - componentType, - skipValidation = false, -}: Pick & { - skipValidation?: boolean -}): ConfigMapSecretUseFormProps => { - const isSecret = componentType === CMSecretComponentType.Secret - - const { - name, - external, - externalType, - type, - mountPath, - defaultMountPath, - subPath, - data, - defaultData, - filePermission, - roleARN, - esoSubPath, - } = configMapSecretData || {} - - const commonInitialValues: ConfigMapSecretUseFormProps = { - name: name ?? '', - isSecret, - external: external ?? false, - externalType: externalType ?? CMSecretExternalType.Internal, - selectedType: type ?? configMapSecretMountDataMap.environment.value, - isFilePermissionChecked: !!filePermission, - isSubPathChecked: !!subPath, - filePermission: filePermission ?? '', - volumeMountPath: mountPath ?? defaultMountPath ?? '', - roleARN: roleARN ?? '', - yamlMode: true, - hasCurrentDataErr: false, - isResolvedData: false, - skipValidation, - currentData: null, - esoSecretYaml: null, - externalSubpathValues: null, - mergeStrategy: null, - secretDataYaml: null, - yaml: null, - } - - if (configMapSecretData) { - const defaultMergeStrategy = isJob || external ? OverrideMergeStrategyType.REPLACE : DEFAULT_MERGE_STRATEGY - const mergeStrategy = - configMapSecretData.mergeStrategy || - (cmSecretStateLabel === CM_SECRET_STATE.INHERITED ? defaultMergeStrategy : null) - - const currentData = processCurrentData({ - configMapSecretData: { ...configMapSecretData, mergeStrategy }, - cmSecretStateLabel, - isSecret, - }) - - return { - ...commonInitialValues, - externalSubpathValues: processExternalSubPathValues({ - data: data || defaultData, - external, - subPath, - esoSubPath, - }), - yaml: convertKeyValuePairToYAML(currentData), - currentData, - mergeStrategy, - ...getSecretDataFromConfigData(configMapSecretData), - } - } - - return { - ...commonInitialValues, - externalSubpathValues: '', - yaml: '"": ""\n', - currentData: CONFIG_MAP_SECRET_DEFAULT_CURRENT_DATA, - esoSecretYaml: '{}', - secretDataYaml: '[]', - mergeStrategy: null, - } -} - -export const getConfigMapSecretReadOnlyValues = ({ - configMapSecretData, - cmSecretStateLabel, - componentType, - isJob, -}: Pick) => { - if (!configMapSecretData) { - return { - configData: [], - data: null, - } - } - - const { - external, - externalType, - esoSecretYaml, - externalSubpathValues, - filePermission, - isSubPathChecked, - roleARN, - secretDataYaml, - selectedType, - volumeMountPath, - yaml, - currentData, - isSecret, - } = getConfigMapSecretFormInitialValues({ - isJob, - configMapSecretData, - cmSecretStateLabel, - componentType, - }) - const mountExistingExternal = - external && externalType === (isSecret ? CMSecretExternalType.KubernetesSecret : CMSecretExternalType.Internal) - - let dataType = '' - if (!isSecret) { - dataType = configMapDataTypeOptions.find(({ value }) => - external && externalType === '' - ? value === CMSecretExternalType.KubernetesConfigMap - : value === externalType, - ).label as string - } else { - dataType = - external && externalType === '' - ? CMSecretExternalType.KubernetesSecret - : (getSelectPickerOptionByValue(getSecretDataTypeOptions(isJob, true), externalType).label as string) - } - - return { - configData: [ - { - displayName: 'DataType', - value: dataType, - key: 'dataType', - }, - { - displayName: 'Mount data as', - value: configMapSecretMountDataMap[selectedType].title, - key: 'mountDataAs', - }, - { - displayName: 'Volume mount path', - value: volumeMountPath, - key: 'volumeMountPath', - }, - { - displayName: 'Set Sub Path', - value: - (configMapSecretMountDataMap[selectedType].value === 'volume' && - (isSubPathChecked ? 'True' : 'False')) || - '', - key: 'setSubPath', - }, - { - displayName: 'Subpath Values', - value: externalSubpathValues, - key: 'externalSubpathValues', - }, - { - displayName: 'File Permission', - value: filePermission, - key: 'filePermission', - }, - { - displayName: 'Role ARN', - value: roleARN, - key: 'roleArn', - }, - ], - data: !mountExistingExternal ? (currentData?.[0]?.k && yaml) || esoSecretYaml || secretDataYaml : null, - } -} // FORM UTILS ---------------------------------------------------------------- // PAYLOAD UTILS ---------------------------------------------------------------- -export const getESOSecretDataFromYAML = (yaml: string): ESOSecretData => { - try { - const json = YAML.parse(yaml) - if (typeof json === 'object') { - const payload = { - secretStore: json.secretStore, - secretStoreRef: json.secretStoreRef, - refreshInterval: json.refreshInterval, - // if null don't send these keys which is achieved by `undefined` - esoData: undefined, - esoDataFrom: undefined, - template: undefined, - } - if (Array.isArray(json?.esoData)) { - payload.esoData = json.esoData - } - if (Array.isArray(json?.esoDataFrom)) { - payload.esoDataFrom = json.esoDataFrom - } - if (typeof json?.template === 'object' && !Array.isArray(json.template)) { - payload.template = json.template - } - return payload - } - return null - } catch { - return null - } -} - -export const getConfigMapSecretPayload = ({ - isSecret, - external, - externalType, - externalSubpathValues, - yaml, - yamlMode, - currentData, - esoSecretYaml, - filePermission, - name, - selectedType, - isFilePermissionChecked, - roleARN, - volumeMountPath, - isSubPathChecked, - mergeStrategy, -}: ConfigMapSecretUseFormProps) => { - const isESO = isSecret && hasESO(externalType) - const _currentData = yamlMode ? convertYAMLToKeyValuePair(yaml) : currentData - const data = _currentData.reduce((acc, curr) => { - if (!curr.k) { - return acc - } - const value = curr.v ?? '' - - return { - ...acc, - [curr.k]: isSecret && externalType === '' ? btoa(value) : value, - } - }, {}) - - const payload: CMSecretPayloadType = { - name, - type: selectedType, - external, - data, - roleARN: null, - externalType: null, - esoSecretData: null, - mountPath: null, - subPath: null, - filePermission: null, - esoSubPath: null, - mergeStrategy, - } - - if ( - (isSecret && externalType === CMSecretExternalType.KubernetesSecret) || - (!isSecret && external) || - (isSecret && isESO) - ) { - delete payload[CODE_EDITOR_RADIO_STATE.DATA] - } - if (isSecret) { - payload.roleARN = '' - payload.externalType = externalType - - if (isESO) { - const esoSecretData = getESOSecretDataFromYAML(esoSecretYaml) - if (esoSecretData) { - payload.esoSecretData = { - secretStore: esoSecretData.secretStore, - esoData: esoSecretData.esoData, - secretStoreRef: esoSecretData.secretStoreRef, - refreshInterval: esoSecretData.refreshInterval, - esoDataFrom: esoSecretData.esoDataFrom, - template: esoSecretData.template, - } - payload.roleARN = roleARN - if (isSubPathChecked && externalSubpathValues) { - payload.esoSubPath = externalSubpathValues.replace(/\s+/g, '').split(',') - } - } - } - } - if (selectedType === configMapSecretMountDataMap.volume.value) { - payload.mountPath = volumeMountPath - payload.subPath = isSubPathChecked - if (isFilePermissionChecked) { - payload.filePermission = filePermission.length === 3 ? `0${filePermission}` : `${filePermission}` - } - - if ( - isSubPathChecked && - ((isSecret && externalType === CMSecretExternalType.KubernetesSecret) || (!isSecret && external)) - ) { - const externalSubpathKey = externalSubpathValues.replace(/\s+/g, '').split(',') - const secretKeys = {} - externalSubpathKey.forEach((key) => { - secretKeys[key] = '' - }) - payload.data = secretKeys - } - } - - return payload -} - const getConfigMapSecretDecodedData = ({ configMapSecretData, isDraft, diff --git a/src/Pages/Shared/ConfigMapSecret/validations.ts b/src/Pages/Shared/ConfigMapSecret/validations.ts deleted file mode 100644 index 4606957937..0000000000 --- a/src/Pages/Shared/ConfigMapSecret/validations.ts +++ /dev/null @@ -1,302 +0,0 @@ -import YAML from 'yaml' - -import { - CMSecretExternalType, - hasESO, - UseFormValidation, - UseFormValidations, - YAMLStringify, -} from '@devtron-labs/devtron-fe-common-lib' - -import { PATTERNS } from '@Config/constants' -import { ValidationRules } from '@Components/cdPipeline/validationRules' - -import { CONFIG_MAP_SECRET_NO_DATA_ERROR, CONFIG_MAP_SECRET_YAML_PARSE_ERROR, SECRET_TOAST_INFO } from './constants' -import { getESOSecretDataFromYAML } from './utils' -import { CMSecretYamlData, ConfigMapSecretUseFormProps } from './types' - -/** - * Validates a YAML string for proper structure and specific key/value constraints. - * - * @param yaml - The YAML string to be validated. - * @returns Use Form Custom Validation - */ -const validateYaml = (yaml: string): UseFormValidation['custom'] => { - try { - // Check if the YAML string is empty or undefined, if so, throw a no-data error. - if (!yaml) { - throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR) - } - - // Parse the YAML string into a JSON object. - const json = YAML.parse(yaml) - - // Ensure the parsed object is of type 'object', otherwise throw an error. - if (typeof json === 'object') { - // Regular expression pattern to validate ConfigMap and Secret keys. - const keyPattern = new RegExp(PATTERNS.CONFIG_MAP_AND_SECRET_KEY) - const errorKeys = [] // To store keys that do not match the key pattern. - const errorValues = [] // To store values that are boolean or numeric, which should be quoted. - - // Iterate through the object keys and validate each key-value pair. - Object.keys(json).forEach((k) => { - // If a key or its corresponding value is empty, throw a no-data error. - if (!k && !json[k]) { - throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR) - } - - // Convert the value to a string for easier validation, handle nested objects using YAMLStringify. - const v = json[k] && typeof json[k] === 'object' ? YAMLStringify(json[k]) : json[k].toString() - - // Check if the key matches the pattern, if not, add it to the errorKeys list. - if (!(k && keyPattern.test(k))) { - errorKeys.push(k) - } - - // If the value is a boolean or number, add it to the errorValues list for improper quoting. - if (v && (typeof json[k] === 'boolean' || typeof json[k] === 'number')) { - errorValues.push(v) - } - }) - - let updatedError = '' - - // If there are invalid keys, append a message listing the problematic keys. - if (errorKeys.length > 0) { - updatedError = `Error: Keys can contain: (Alphanumeric) (-) (_) (.) | Invalid key(s): ${errorKeys - .map((e) => `"${e}"`) - .join(', ')}` - } - - // If there are boolean or numeric values not wrapped in quotes, append another error message. - if (errorValues.length > 0) { - if (updatedError !== '') { - updatedError += '\n' // Separate key and value error messages by a new line. - } - updatedError += `Error: Boolean and numeric values must be wrapped in double quotes Eg. ${errorValues - .map((e) => `"${e}"`) - .join(', ')}` - } - - // Return the validation result - return { - isValid: () => !updatedError, // Validation is valid if no error messages are present. - message: updatedError, // The generated error message (if any). - } - } - - // If the parsed result is not an object, throw a yaml parse error. - throw new Error(CONFIG_MAP_SECRET_YAML_PARSE_ERROR) - } catch (err) { - // Catch any errors, and return an invalid result with a relevant message. - return { - // Always return false when an error occurs. - isValid: () => false, - // Display a parsing error. - message: - err.name === 'YAMLParseError' ? err.message.replace(/[\s]+/g, ' ') : CONFIG_MAP_SECRET_YAML_PARSE_ERROR, - } - } -} - -/** - * Validates a YAML string representing an External Secret Operator (ESO) secret configuration. - * - * @param esoSecretYaml - The YAML string to be validated. - * @returns Use Form Validation - */ -const validateEsoSecretYaml = (esoSecretYaml: string): UseFormValidation => { - try { - // Check if the provided YAML string is empty or undefined, and throw a no-data error. - if (!esoSecretYaml) { - throw new Error(CONFIG_MAP_SECRET_NO_DATA_ERROR) - } - - // Parse the YAML string into a JSON object. - const json = getESOSecretDataFromYAML(esoSecretYaml) - - // Ensure the parsed result is non-null before proceeding with further validation. - if (json) { - // Validation logic: - // 1. Ensure only one of 'esoData' or 'esoDataFrom' is provided, not both. - // 2. Either 'esoData' or 'esoDataFrom' must be provided (at least one is required). - // 3. Ensure that exactly one of 'secretStore' or 'secretStoreRef' is provided (not both or neither). - let isValid = - !(json.esoData && json.esoDataFrom) && // Rule 1: Only one of 'esoData' or 'esoDataFrom' - (json.esoData || json.esoDataFrom) && // Rule 2: At least one must be present - !json.secretStore !== !json.secretStoreRef // Rule 3: Exactly one of 'secretStore' or 'secretStoreRef' - let errorMessage = '' - - // Validation logic for 'esoData' array: - // 1. Check if 'esoData' exists and the previous validation ('isValid') passed. - // 2. For each entry in 'esoData', ensure both 'secretKey' and 'key' are present and truthy. - // If any entry is missing either 'secretKey' or 'key', set 'isValid' to false. - if (json.esoData && isValid) { - isValid = json.esoData?.reduce( - (_isValid, s) => - // Rule: Both 'secretKey' and 'key' must exist and be truthy for each entry. - _isValid && !!s?.secretKey && !!s.key, - isValid, // Keep track of overall validity - ) - } - - // Set error messages based on the provided 'secretStore', 'secretStoreRef', and 'esoData'/'esoDataFrom': - if (json.esoDataFrom && json.esoData) { - // Error: Both 'esoData' and 'esoDataFrom' are provided, which is invalid. - errorMessage = SECRET_TOAST_INFO.BOTH_ESO_DATA_AND_DATA_FROM_AVAILABLE - } else if (!json.esoDataFrom && !json.esoData) { - // Error: Neither 'esoData' nor 'esoDataFrom' is provided, at least one is required. - errorMessage = SECRET_TOAST_INFO.BOTH_ESO_DATA_AND_DATA_FROM_UNAVAILABLE - } else if (json.secretStore && json.secretStoreRef) { - // Error: Both 'secretStore' and 'secretStoreRef' are provided, only one should be. - errorMessage = SECRET_TOAST_INFO.BOTH_STORE_AVAILABLE - } else if (json.secretStore || json.secretStoreRef) { - // Valid: One of 'secretStore' or 'secretStoreRef' is provided, but ensure the keys are correct. - errorMessage = SECRET_TOAST_INFO.CHECK_KEY_SECRET_KEY - } else { - // Error: Neither 'secretStore' nor 'secretStoreRef' is provided, one is required. - errorMessage = SECRET_TOAST_INFO.BOTH_STORE_UNAVAILABLE - } - - // Return the validation result with a custom validator and the corresponding error message. - return { - custom: { - isValid: () => isValid, // Validation is successful if all checks pass. - message: errorMessage, // Error message based on the validation logic. - }, - } - } - - // If the parsed result is not an object, throw a yaml parse error. - throw new Error(CONFIG_MAP_SECRET_YAML_PARSE_ERROR) - } catch (err) { - // Catch any errors and return an invalid result with an appropriate error message. - return { - custom: { - // Always return false when an error occurs. - isValid: () => false, - // Display a parsing error message. - message: - err.name === 'YAMLParseError' - ? err.message.replace(/[\s]+/g, ' ') - : CONFIG_MAP_SECRET_YAML_PARSE_ERROR, - }, - } - } -} - -export const getConfigMapSecretFormValidations: UseFormValidations = ({ - isSecret, - external, - externalType, - selectedType, - isFilePermissionChecked, - volumeMountPath, - isSubPathChecked, - yaml, - yamlMode, - esoSecretYaml, - skipValidation, -}) => { - if (skipValidation) { - return {} - } - - const mountExistingExternal = - external && externalType === (isSecret ? CMSecretExternalType.KubernetesSecret : CMSecretExternalType.Internal) - const isESO = isSecret && hasESO(externalType) - - const rules = new ValidationRules() - - return { - name: { - required: true, - pattern: { - value: PATTERNS.CONFIGMAP_AND_SECRET_NAME, - message: - "Name must start and end with an alphanumeric character. It can contain only lowercase alphanumeric characters, '-' or '.'", - }, - custom: { - isValid: (value) => value.length <= 253, - message: 'More than 253 characters are not allowed', - }, - }, - ...(selectedType === 'volume' - ? { - volumeMountPath: { - required: true, - custom: { - isValid: (value) => rules.cmVolumeMountPath(value).isValid, - message: rules.cmVolumeMountPath(volumeMountPath).message, - }, - }, - ...(isFilePermissionChecked - ? { - filePermission: { - required: true, - pattern: { - value: PATTERNS.ALL_DIGITS_BETWEEN_0_AND_7, - message: 'This is octal number, use numbers between 0 to 7', - }, - custom: [ - { - isValid: (value) => value.length <= 4, - message: 'More than 4 characters are not allowed', - }, - { - isValid: (value) => value.length !== 4 || value.startsWith('0'), - message: - '4 characters are allowed in octal format only, first character should be 0', - }, - { - isValid: (value) => value.length >= 3, - message: 'Atleast 3 character are required', - }, - ], - }, - } - : {}), - ...(isSubPathChecked && ((isSecret && externalType === 'KubernetesSecret') || (!isSecret && external)) - ? { - externalSubpathValues: { - required: true, - pattern: { - value: PATTERNS.CONFIG_MAP_AND_SECRET_MULTPLS_KEYS, - message: 'Use (a-z), (0-9), (-), (_),(.); Use (,) to separate multiple keys', - }, - }, - } - : {}), - } - : {}), - ...(!isESO && !mountExistingExternal - ? { - ...(yamlMode - ? { - yaml: { - custom: validateYaml(yaml), - }, - } - : { - hasCurrentDataErr: { - custom: { - isValid: (value) => !value, - message: 'Please resolve the errors before saving', - }, - }, - currentData: { - custom: { - isValid: (value: CMSecretYamlData[]) => !!value.filter(({ k }) => !!k).length, - message: CONFIG_MAP_SECRET_NO_DATA_ERROR, - }, - }, - }), - } - : {}), - ...(isSecret && isESO - ? { - esoSecretYaml: validateEsoSecretYaml(esoSecretYaml), - } - : {}), - } -} diff --git a/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx b/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx index cde17139e8..9fa4ed586c 100644 --- a/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx +++ b/src/Pages/Shared/EnvironmentOverride/EnvironmentOverride.tsx @@ -16,7 +16,12 @@ import { useState, useEffect } from 'react' -import { ApprovalConfigDataKindType, getIsApprovalPolicyConfigured, Reload } from '@devtron-labs/devtron-fe-common-lib' +import { + ApprovalConfigDataKindType, + getIsApprovalPolicyConfigured, + Reload, + CMSecretComponentType, +} from '@devtron-labs/devtron-fe-common-lib' import { useParams, useRouteMatch, @@ -31,7 +36,6 @@ import { import { mapByKey, ErrorBoundary, useAppContext } from '@Components/common' import { APP_COMPOSE_STAGE, URLS, getAppComposeURL } from '@Config/index' import { ConfigMapSecretWrapper } from '@Pages/Shared/ConfigMapSecret/ConfigMapSecret.wrapper' -import { CMSecretComponentType } from '@Pages/Shared/ConfigMapSecret/types' import { DeploymentTemplate } from '@Pages/Applications' import { ComponentStates, EnvironmentOverrideComponentProps } from './EnvironmentOverrides.types' diff --git a/src/assets/icons/ic-variable.svg b/src/assets/icons/ic-variable.svg deleted file mode 100644 index e3818236a0..0000000000 --- a/src/assets/icons/ic-variable.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/src/assets/icons/ic-view-variable-toggle.svg b/src/assets/icons/ic-view-variable-toggle.svg deleted file mode 100644 index 3c7ec5400e..0000000000 --- a/src/assets/icons/ic-view-variable-toggle.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - diff --git a/src/assets/icons/ic-view-variables.svg b/src/assets/icons/ic-view-variables.svg deleted file mode 100644 index 46109dd8e5..0000000000 --- a/src/assets/icons/ic-view-variables.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index fadd0cd19c..8035b43536 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -23,7 +23,6 @@ import { VisibleModal, Drawer, DeleteDialog, - RefVariableType, VariableType, MandatoryPluginDataType, ButtonWithLoader, @@ -48,11 +47,11 @@ import { ResourceKindType, uploadCIPipelineFile, getGlobalVariables, + FloatingVariablesSuggestions, TriggerType, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' import { - FloatingVariablesSuggestions, getParsedBranchValuesForPlugin, getPluginIdsFromBuildStage, importComponentFromFELibrary, diff --git a/src/components/cdPipeline/CDPipeline.tsx b/src/components/cdPipeline/CDPipeline.tsx index c46939e9b6..0756295650 100644 --- a/src/components/cdPipeline/CDPipeline.tsx +++ b/src/components/cdPipeline/CDPipeline.tsx @@ -47,6 +47,7 @@ import { getEnvironmentListMinPublic, uploadCDPipelineFile, getGlobalVariables, + FloatingVariablesSuggestions, saveCDPipeline, TriggerType, } from '@devtron-labs/devtron-fe-common-lib' @@ -56,7 +57,6 @@ import { ReactComponent as ICWarning } from '@Icons/ic-warning.svg' import { ReactComponent as Close } from '../../assets/icons/ic-close.svg' import { CDDeploymentTabText, RegistryPayloadType, SourceTypeMap, ViewType } from '../../config' import { - FloatingVariablesSuggestions, getPluginIdsFromBuildStage, importComponentFromFELibrary, sortObjectArrayAlphabetically, diff --git a/src/components/cdPipeline/validationRules.ts b/src/components/cdPipeline/validationRules.ts deleted file mode 100644 index a84f1b4e4a..0000000000 --- a/src/components/cdPipeline/validationRules.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { PATTERNS } from '../../config' -import { - CHARACTER_ERROR_MAX, - CHARACTER_ERROR_MIN, - ERROR_MESSAGE_FOR_VALIDATION, - INVALID_VOLUME_MOUNT_PATH_IN_CM_CS, - REQUIRED_FIELD_MSG, -} from '../../config/constantMessaging' - -export class ValidationRules { - name = (value: string, pattern?: string): { isValid: boolean; message: string } => { - const regExp = new RegExp(pattern || PATTERNS.APP_NAME) - if (value.length === 0) { - return { isValid: false, message: REQUIRED_FIELD_MSG } - } - if (value.length < 2) { - return { isValid: false, message: CHARACTER_ERROR_MIN } - } - if (value.length > 50) { - return { isValid: false, message: CHARACTER_ERROR_MAX } - } - if (!regExp.test(value)) { - return { - isValid: false, - message: ERROR_MESSAGE_FOR_VALIDATION, - } - } - return { isValid: true, message: '' } - } - - environment = (id: number): { isValid: boolean; message: string } => { - if (!id) { - return { isValid: false, message: REQUIRED_FIELD_MSG } - } - return { isValid: true, message: null } - } - - isGitProvider = (material) => { - if (material.gitProviderId) { - return { isValid: true, message: '' } - } - return { isValid: false, message: REQUIRED_FIELD_MSG } - } - - namespace = (name: string): { isValid: boolean; message: string } => { - return this.name(name, PATTERNS.NAMESPACE) - } - - containerRegistry = (containerRegistry: string): { isValid: boolean; message: string } => { - if (!containerRegistry.length) { - return { isValid: false, message: REQUIRED_FIELD_MSG } - } - return { isValid: true, message: null } - } - - repository = (repository: string): { isValid: boolean; message: string } => { - if (!repository.length) { - return { isValid: false, message: REQUIRED_FIELD_MSG } - } - return { isValid: true, message: null } - } - - cmVolumeMountPath = (value: string): { isValid: boolean; message: string } => { - const re = PATTERNS.ALPHANUMERIC_WITH_SPECIAL_CHAR_AND_SLASH - const regExp = new RegExp(re) - const test = regExp.test(value) - if (!test) { - return { - isValid: false, - message: INVALID_VOLUME_MOUNT_PATH_IN_CM_CS, - } - } - return { isValid: true, message: '' } - } -} diff --git a/src/components/common/FloatingVariablesSuggestions/FloatingVariablesSuggestions.tsx b/src/components/common/FloatingVariablesSuggestions/FloatingVariablesSuggestions.tsx deleted file mode 100644 index be6345d08c..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/FloatingVariablesSuggestions.tsx +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { useState, useRef, useMemo, useCallback, memo, useEffect } from 'react' -import { useAsync, useWindowSize } from '@devtron-labs/devtron-fe-common-lib' -import Draggable from 'react-draggable' -import Tippy from '@tippyjs/react' -import Suggestions from './Suggestions' -import { getScopedVariables } from './service' -import { FloatingVariablesSuggestionsProps } from './types' -import { ReactComponent as ICDrag } from '../../../assets/icons/drag.svg' -import { ReactComponent as ICVariable } from '../../../assets/icons/ic-variable.svg' -import { SUGGESTIONS_SIZE } from './constants' - -/** - * Component uses react-draggable and handles the re-sizing and positioning of the suggestions on the assumption that the suggestions are going to expand to the right and bottom of the collapsed state - * @param zIndex - To Set the zIndex of the suggestions - * @param appId - To fetch the scoped variables - * @param envId - (Optional) - * @param clusterId - (Optional) - * @param bounds - (Optional) To set the bounds of the suggestions - * @param hideObjectVariables - (Optional) To hide the object/array variables, default is true - * @returns - */ -const FloatingVariablesSuggestions = ({ - zIndex, - appId, - envId, - clusterId, - bounds, - hideObjectVariables = true, -}: FloatingVariablesSuggestionsProps) => { - const [isActive, setIsActive] = useState(false) - const [collapsedPosition, setCollapsedPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 }) - const [expandedPosition, setExpandedPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 }) - - const [loadingScopedVariables, variablesData, error, reloadScopedVariables] = useAsync( - () => getScopedVariables(appId, envId, clusterId, hideObjectVariables), - [appId, envId, clusterId], - ) - - const windowSize = useWindowSize() - // In case of StrictMode, we get error findDOMNode is deprecated in StrictMode - // So we use useRef to get the DOM node - const nodeRef = useRef(null) - - // nodeRef.current is dependency even though its a ref as initially its null and we need to get the - // first value that it gets and after that is not going to trigger again - const initialPosition = useMemo(() => { - const initialPosition = nodeRef.current?.getBoundingClientRect() || { - x: 0, - y: 0, - } - return { x: initialPosition.x, y: initialPosition.y } - }, [nodeRef.current]) - - // The size of the active state can expand say in case user expands SuggestionsInfo and the widget is at bottom of screen - useEffect(() => { - const resizeObserver = new ResizeObserver((entries) => { - if (entries?.length > 0 && isActive) { - const { height } = entries[0].contentRect - if (initialPosition.y + expandedPosition.y + height > windowSize.height) { - setExpandedPosition({ - x: expandedPosition.x, - y: windowSize.height - height - initialPosition.y, - }) - } - } - }) - resizeObserver.observe(nodeRef.current) - return () => { - resizeObserver.disconnect() - } - }, [isActive, expandedPosition, windowSize, initialPosition]) - - const handleActivation = () => { - const currentPosInScreen = { - x: initialPosition.x + collapsedPosition.x, - y: initialPosition.y + collapsedPosition.y, - } - - setExpandedPosition({ - x: collapsedPosition.x, - y: collapsedPosition.y, - }) - - if (currentPosInScreen.y > windowSize.height - SUGGESTIONS_SIZE.height) { - setExpandedPosition({ - x: collapsedPosition.x, - y: windowSize.height - SUGGESTIONS_SIZE.height - initialPosition.y, - }) - } - - if (currentPosInScreen.x > windowSize.width - SUGGESTIONS_SIZE.width) { - setExpandedPosition({ - x: windowSize.width - SUGGESTIONS_SIZE.width - initialPosition.x, - y: collapsedPosition.y, - }) - } - - if ( - currentPosInScreen.x > windowSize.width - SUGGESTIONS_SIZE.width && - currentPosInScreen.y > windowSize.height - SUGGESTIONS_SIZE.height - ) { - setExpandedPosition({ - x: windowSize.width - SUGGESTIONS_SIZE.width - initialPosition.x, - y: windowSize.height - SUGGESTIONS_SIZE.height - initialPosition.y, - }) - } - - setIsActive(true) - } - - // Need to memoize this function since it is passed as a prop to Suggestions - const handleDeActivation = useCallback((e: React.MouseEvent) => { - e.stopPropagation() - setIsActive(false) - }, []) - - // e will be unused, but we need to pass it as a parameter since Draggable expects it - const handleCollapsedDrag = (e, data: { x: number; y: number }) => { - const currentPosInScreen = { - x: initialPosition.x + data.x, - y: initialPosition.y + data.y, - } - if ( - currentPosInScreen.y < 0 || - currentPosInScreen.x < 0 || - currentPosInScreen.x + nodeRef.current?.getBoundingClientRect().width > windowSize.width || - currentPosInScreen.y + nodeRef.current?.getBoundingClientRect().height > windowSize.height - ) { - return - } - - setCollapsedPosition(data) - } - - const handleExpandedDrag = (e, data: { x: number; y: number }) => { - const currentPosInScreen = { - x: initialPosition.x + data.x, - y: initialPosition.y + data.y, - } - if ( - currentPosInScreen.y < 0 || - currentPosInScreen.x < 0 || - currentPosInScreen.x + nodeRef.current?.getBoundingClientRect().width > windowSize.width || - currentPosInScreen.y + nodeRef.current?.getBoundingClientRect().height > windowSize.height - ) { - return - } - setExpandedPosition(data) - // Only Need to retain the collapsed position if the user has not dragged the suggestions, so need to update - setCollapsedPosition(data) - } - - if (!isActive) { - return ( - -
- - - - - -
-
- ) - } - - return ( - -
- -
-
- ) -} - -// This would save API call if the props are same -export default memo(FloatingVariablesSuggestions) diff --git a/src/components/common/FloatingVariablesSuggestions/SuggestionItem.tsx b/src/components/common/FloatingVariablesSuggestions/SuggestionItem.tsx deleted file mode 100644 index 29854ec782..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/SuggestionItem.tsx +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useState } from 'react' -import Tippy from '@tippyjs/react' -import DOMPurify from 'dompurify' -import { ClipboardButton, copyToClipboard, YAMLStringify } from '@devtron-labs/devtron-fe-common-lib' -import { SuggestionsItemProps } from './types' -import { NO_DEFINED_DESCRIPTION } from './constants' - -export default function SuggestionItem({ - variableName, - description, - variableValue, - isRedacted, - highlightText, -}: SuggestionsItemProps) { - const [copyToClipboardPromise, setCopyToClipboardPromise] = useState>(null) - - const clipboardContent = `@{{${variableName}}}` - - const handleCopyTrigger = () => { - setCopyToClipboardPromise(copyToClipboard(clipboardContent)) - } - - const sanitiseVariableValue = (value): JSX.Element => { - if (isRedacted) { - return is sensitive & hidden - } - if (value === '') { - return

'""'

- } - if (typeof value === 'boolean') { - return

{value ? 'true' : 'false'}

- } - if (typeof value === 'object') { - return
{YAMLStringify(value)}
- } - return

{value}

- } - - const highlightedText = (text: string): string => { - if (highlightText === '') { - return text - } - - try { - const regex = new RegExp(highlightText, 'gi') - return text.replace(regex, (match) => `${match}`) - } catch (error) { - return text - } - } - - const renderDescription = (): JSX.Element => { - if (description === NO_DEFINED_DESCRIPTION) { - return

{description}

- } - - return ( -

- ) - } - - const renderVariableName = (): JSX.Element => ( -

- ) - - return ( - -

Value
-
- {sanitiseVariableValue(variableValue)} -
-
- } - placement="left" - interactive - // Have to append to body because the parent is draggable - appendTo={document.body} - > -
-
- {renderVariableName()} - - -
- -
{renderDescription()}
-
- - ) -} diff --git a/src/components/common/FloatingVariablesSuggestions/Suggestions.tsx b/src/components/common/FloatingVariablesSuggestions/Suggestions.tsx deleted file mode 100644 index 8d3850bbfe..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/Suggestions.tsx +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useState, memo, useEffect } from 'react' -import { - GenericEmptyState, - Progressing, - Reload, - DebouncedSearch, - Button, - ComponentSizeType, - ButtonVariantType, - ButtonStyleType, -} from '@devtron-labs/devtron-fe-common-lib' -import SuggestionItem from './SuggestionItem' -import { ReactComponent as ICClose } from '../../../assets/icons/ic-cross.svg' -import { ReactComponent as ICSearch } from '../../../assets/icons/ic-search.svg' -import { ReactComponent as ICVariable } from '../../../assets/icons/ic-variable.svg' -import NoVariables from '../../../assets/img/no-artifact.webp' -import { SuggestionsProps, ScopedVariableType } from './types' -import SuggestionsInfo from './SuggestionsInfo' -import { NO_DEFINED_DESCRIPTION, NO_DEFINED_VALUE } from './constants' - -const Suggestions = ({ handleDeActivation, loading, variables, reloadVariables, error }: SuggestionsProps) => { - const [suggestions, setSuggestions] = useState(variables) - const [clearSearch, setClearSearch] = useState(false) - const [highlightText, setHighlightText] = useState('') - - const enableSearch = !loading && !error && !!variables?.length - - useEffect(() => { - setSuggestions(variables) - }, [variables]) - - const onSearch = (text: string) => { - // No need to check if variables exists since we are not even showing search bar if there are no variables - const trimmedText = text.trim().toLowerCase() - const filteredSuggestions = variables.filter( - (variable) => - variable.variableName.toLowerCase().includes(trimmedText) || - variable.shortDescription?.toLowerCase().includes(trimmedText), - ) - setSuggestions(filteredSuggestions) - setHighlightText(trimmedText) - } - - const handleClearSearch = () => { - setClearSearch(!clearSearch) - } - - const renderClearSearchButton = (): JSX.Element => ( - - ) - - const renderHeader = (): JSX.Element => ( -
-
-
-
-

Scoped variables

- - -
- -

Use variable to set dynamic value

-
- -
-
-
- - {enableSearch && ( -
{ - if (e.key === 'Enter') { - e.preventDefault() - } - }} - className="flexbox dc__align-self-stretch pt-8 pb-8 pl-12 pr-12 bg__primary" - > - -
- )} -
- ) - - const renderSuggestions = (): JSX.Element => ( - <> -
- {suggestions.length ? ( - suggestions.map((variable) => ( - - )) - ) : ( - - )} -
- - - - ) - - const renderBody = (): JSX.Element => { - if (loading) { - return ( -
- -
- ) - } - - if (variables?.length === 0) { - return ( - <> -
- -
- - - - ) - } - - if (!enableSearch) { - return - } - - return renderSuggestions() - } - - return ( - <> - {renderHeader()} - {renderBody()} - - ) -} - -export default memo(Suggestions) diff --git a/src/components/common/FloatingVariablesSuggestions/SuggestionsInfo.tsx b/src/components/common/FloatingVariablesSuggestions/SuggestionsInfo.tsx deleted file mode 100644 index 37cd519029..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/SuggestionsInfo.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { memo, useState } from 'react' -import { Link } from 'react-router-dom' -import { URLS } from '@devtron-labs/devtron-fe-common-lib' -import { ReactComponent as ICHelp } from '../../../assets/icons/ic-help.svg' -import { ReactComponent as ICDown } from '../../../assets/icons/ic-chevron-down.svg' -import { SUGGESTIONS_INFO_TITLE } from './constants' - -const SuggestionsInfo = () => { - const [expanded, setExpanded] = useState(false) - - const handleExpansion = () => { - setExpanded(!expanded) - } - - return ( -
- - - {expanded && ( -
-

- Use a scoped variable for dynamic values, which are defined in the  - - Global Configuration - - . To use a variable, type  - {'@{{variablename}}'}. -

-
- )} -
- ) -} - -export default memo(SuggestionsInfo) diff --git a/src/components/common/FloatingVariablesSuggestions/__tests__/FloatingVariablesSuggestions.test.tsx b/src/components/common/FloatingVariablesSuggestions/__tests__/FloatingVariablesSuggestions.test.tsx deleted file mode 100644 index 0deddcf3d2..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/__tests__/FloatingVariablesSuggestions.test.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react' -import { render, screen, fireEvent } from '@testing-library/react' -import { useAsync } from '@devtron-labs/devtron-fe-common-lib' -import FloatingVariablesSuggestions from '../FloatingVariablesSuggestions' - -// Mocking suggestions items since its already tested -jest.mock( - '../Suggestions', - () => - function Suggestions() { - return
- }, -) - -jest.mock('@devtron-labs/devtron-fe-common-lib', () => ({ - useAsync: jest.fn(), -})) - -window.ResizeObserver = - window.ResizeObserver || - jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn(), - })) - -describe('When FloatingVariablesSuggestions mounts', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should show collapsed state by default', () => { - ;(useAsync as jest.Mock).mockReturnValue([true, null, null, null]) - const { getByTestId } = render() - expect(getByTestId('collapsed-state')).toBeTruthy() - }) - - it('should allow dragging collapsed state on drag of handle-drag', () => { - ;(useAsync as jest.Mock).mockReturnValue([true, null, null, null]) - const { getByTestId, container } = render() - const initialPosition = getByTestId('collapsed-state').getBoundingClientRect() - const dragButton = container.querySelector('.handle-drag') - fireEvent.mouseDown(dragButton) - fireEvent.mouseMove(dragButton, { clientX: 100, clientY: 100 }) - fireEvent.mouseUp(dragButton) - const finalPosition = getByTestId('collapsed-state').getBoundingClientRect() - expect(initialPosition).not.toBe(finalPosition) - }) - - it('should show expanded state on click of activate-suggestions', () => { - ;(useAsync as jest.Mock).mockReturnValue([true, null, null, null]) - const { getByTestId } = render() - fireEvent.click(getByTestId('activate-suggestions')) - expect(screen.queryByTestId('collapsed-state')).toBeFalsy() - }) -}) diff --git a/src/components/common/FloatingVariablesSuggestions/__tests__/Suggestions.test.tsx b/src/components/common/FloatingVariablesSuggestions/__tests__/Suggestions.test.tsx deleted file mode 100644 index 1b428013bd..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/__tests__/Suggestions.test.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react' -import { render, screen, fireEvent, act } from '@testing-library/react' -import Suggestions from '../Suggestions' -import { mockVariable } from '../mocks' - -// Mocking suggestions items since its already tested -jest.mock( - '../SuggestionItem', - () => - function SuggestionItem() { - return
- }, -) - -// mocking SuggestionsInfo since its already tested -jest.mock( - '../SuggestionsInfo', - () => - function SuggestionsInfo() { - return
- }, -) - -describe('When Suggestions mounts', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should show all the variables', () => { - const { getByTestId } = render( - , - ) - expect(getByTestId('suggestion-item')).toBeTruthy() - }) - - it('should call handleDeActivation on click of deactivate-suggestions', () => { - const handleDeActivation = jest.fn() - const { getByTestId } = render( - , - ) - getByTestId('deactivate-suggestions').click() - expect(handleDeActivation).toBeCalled() - }) - - it('should show loader if loading is true', () => { - const { getByTestId } = render( - , - ) - - expect(getByTestId('progressing')).toBeTruthy() - }) - - it('should show No variables found if there are no variables', () => { - render( - , - ) - expect(screen.getByText('No variables found')).toBeTruthy() - }) - - it('should show no matching variable found if there are no matching variables', async () => { - render( - , - ) - fireEvent.change(screen.getByTestId('debounced-search'), { target: { value: 'test' } }) - await act(async () => { - await new Promise((res) => setTimeout(res, 500)) - }) - expect(screen.getByText('No matching variable found')).toBeTruthy() - }) -}) diff --git a/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsInfo.test.tsx b/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsInfo.test.tsx deleted file mode 100644 index dd5c270fcf..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsInfo.test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react' -import { render, screen, fireEvent } from '@testing-library/react' -import { createMemoryHistory } from 'history' -import { Router } from 'react-router-dom' -import SuggestionsInfo from '../SuggestionsInfo' - -const history = createMemoryHistory() - -describe('When SuggestionsInfo mounts', () => { - beforeEach(() => { - jest.clearAllMocks() - history.push('/') - }) - - it('should have a button', () => { - render( - - - , - ) - expect(screen.getByRole('button')).toBeTruthy() - }) - - it('should expand content when button is clicked', () => { - render( - - - , - ) - fireEvent.click(screen.getByRole('button')) - expect(screen.getByText('@{{variablename}}')).toBeTruthy() - }) - - it('should collapse content when button is clicked twice', () => { - render( - - - , - ) - fireEvent.click(screen.getByRole('button')) - expect(screen.queryByText('@{{variablename}}')).toBeTruthy() - fireEvent.click(screen.getByRole('button')) - expect(screen.queryByText('@{{variablename}}')).toBeFalsy() - }) -}) diff --git a/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsItem.test.tsx b/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsItem.test.tsx deleted file mode 100644 index 792bd14044..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/__tests__/SuggestionsItem.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react' -import { render, screen } from '@testing-library/react' -import SuggestionItem from '../SuggestionItem' -import { NO_DEFINED_DESCRIPTION } from '../constants' - -// mocking ClipboardButton since its already tested -jest.mock('@devtron-labs/devtron-fe-common-lib', () => ({ - ClipboardButton: function ClipboardButton(triggerCopy) { - if (triggerCopy) return
Copied
- return
ClipboardButton
- }, -})) - -describe('When SuggestionsItem mounts', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should show description', () => { - render( - , - ) - expect(screen.getByText('description')).toBeTruthy() - }) - - it('should highlight description if highlightText is present', () => { - render( - , - ) - // since desc would be in span and differentiated we only need to check if desc is present - expect(screen.getByText('desc')).toBeTruthy() - expect(screen.getByText('desc').tagName).toBe('SPAN') - }) - - it('should show copied on click of suggestion item', () => { - const { getByTestId } = render( - , - ) - getByTestId('suggestion-item').click() - expect(screen.getByText('Copied')).toBeTruthy() - }) - - it('should show NO_DEFINED_DESCRIPTION if description is not present', () => { - render( - , - ) - expect(screen.getByText(NO_DEFINED_DESCRIPTION)).toBeTruthy() - }) -}) diff --git a/src/components/common/FloatingVariablesSuggestions/__tests__/service.test.ts b/src/components/common/FloatingVariablesSuggestions/__tests__/service.test.ts deleted file mode 100644 index 1d2bb79be7..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/__tests__/service.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { get } from '@devtron-labs/devtron-fe-common-lib' -import { getScopedVariables } from '../service' -import { Routes } from '../../../../config' - -jest.mock('@devtron-labs/devtron-fe-common-lib', () => ({ - get: jest.fn(), -})) - -describe('getScopedVariables', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should call get with correct params if only appId is sent', async () => { - ;(get as jest.Mock).mockResolvedValueOnce({}) - const appId = 'appId' - const query = `?appId=${appId}&scope={"appId":${appId}}` - await getScopedVariables(appId, null, null) - expect(get).toHaveBeenCalledWith(`${Routes.SCOPED_GLOBAL_VARIABLES}${query}`) - }) - - it('should call get with correct params if all the entries are sent', async () => { - ;(get as jest.Mock).mockResolvedValueOnce({}) - const appId = 'appId' - const envId = 'envId' - const clusterId = 'clusterId' - const query = `?appId=${appId}&scope={"appId":${appId},"envId":${envId},"clusterId":${clusterId}}` - await getScopedVariables(appId, envId, clusterId) - expect(get).toHaveBeenCalledWith(`${Routes.SCOPED_GLOBAL_VARIABLES}${query}`) - }) -}) diff --git a/src/components/common/FloatingVariablesSuggestions/constants.ts b/src/components/common/FloatingVariablesSuggestions/constants.ts deleted file mode 100644 index d2c1d6c5cd..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/constants.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const SUGGESTIONS_SIZE = { - width: 356, - height: 504, -} - -export const NO_DEFINED_DESCRIPTION = 'No Defined Description' -export const NO_DEFINED_VALUE = 'No Defined Value' -export const SUGGESTIONS_INFO_TITLE = 'What is scoped variable?' diff --git a/src/components/common/FloatingVariablesSuggestions/mocks.ts b/src/components/common/FloatingVariablesSuggestions/mocks.ts deleted file mode 100644 index da8eacef3b..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/mocks.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const mockVariable = [{ variableName: 'name', shortDescription: 'desc', isRedacted: true }] diff --git a/src/components/common/FloatingVariablesSuggestions/service.ts b/src/components/common/FloatingVariablesSuggestions/service.ts deleted file mode 100644 index 28105454a0..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/service.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { get, ResponseType } from '@devtron-labs/devtron-fe-common-lib' -import { Routes } from '../../../config' -import { ScopedVariableType } from './types' - -const generateScope = (key, value) => { - if (key && value) { - return `"${key}":${value},` - } - return '' -} - -export const getScopedVariables = async ( - appId, - envId, - clusterId, - hideObjectVariables: boolean = true, -): Promise => { - let query = `?appId=${appId}&scope={` - - query += generateScope('appId', appId) - query += generateScope('envId', envId) - query += generateScope('clusterId', clusterId) - - if (query.endsWith(',')) { - query = query.slice(0, -1) - } - - query += '}' - - const { result } = (await get(`${Routes.SCOPED_GLOBAL_VARIABLES}${query}`)) as ResponseType - if (!result) { - return [] - } - - if (hideObjectVariables) { - return result.filter((variable) => !variable.variableValue || typeof variable.variableValue.value !== 'object') - } - - return result -} diff --git a/src/components/common/FloatingVariablesSuggestions/types.ts b/src/components/common/FloatingVariablesSuggestions/types.ts deleted file mode 100644 index a3877f6e69..0000000000 --- a/src/components/common/FloatingVariablesSuggestions/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024. Devtron Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { DraggableBounds } from 'react-draggable' - -export interface ScopedVariableType { - variableName: string - shortDescription: string | null - variableValue?: { - value: string | number | boolean | Record - } | null - isRedacted: boolean -} - -export interface FloatingVariablesSuggestionsProps { - zIndex: number - appId: string - envId?: string - clusterId?: string - bounds?: DraggableBounds | string | false - /** - * This will hide the variables with object/array values if set to true - * @default - true - */ - hideObjectVariables?: boolean -} - -export interface SuggestionsItemProps { - variableName: string - description: string - variableValue: Required - isRedacted: boolean - highlightText: string -} - -export interface SuggestionsProps { - handleDeActivation: (e: React.MouseEvent) => void - loading: boolean - variables: ScopedVariableType[] - reloadVariables: () => void - error: boolean -} diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 14b4121b11..6cb87c5a0f 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -35,6 +35,5 @@ export * from './DatePickers/Calender' export * from './DatePickers/DayPickerRangeController' export * from './helpers/compareVersion' export * from './hooks/FileReader' -export { default as FloatingVariablesSuggestions } from './FloatingVariablesSuggestions/FloatingVariablesSuggestions' export { default as HiddenInput } from './HiddenInput/HiddenInput' export * from './TLSConnectionForm' diff --git a/src/components/scopedVariables/service.ts b/src/components/scopedVariables/service.ts index 412424475b..d68926c3aa 100644 --- a/src/components/scopedVariables/service.ts +++ b/src/components/scopedVariables/service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { get, post } from '@devtron-labs/devtron-fe-common-lib' +import { get, post, ROUTES as COMMON_ROUTES } from '@devtron-labs/devtron-fe-common-lib' import { ScopedVariablesDataType } from './types' import { Routes } from '../../config' @@ -25,5 +25,5 @@ export const postScopedVariables = (scopedVariables: ScopedVariablesDataType) => const payload = { manifest: scopedVariables, } - return post(Routes.SCOPED_GLOBAL_VARIABLES, payload) + return post(COMMON_ROUTES.SCOPED_GLOBAL_VARIABLES, payload) } diff --git a/src/config/constantMessaging.ts b/src/config/constantMessaging.ts index 7ffa1348dc..9998f13446 100644 --- a/src/config/constantMessaging.ts +++ b/src/config/constantMessaging.ts @@ -64,7 +64,6 @@ export const enum DeleteComponentsName { export const LEARN_MORE = 'Learn more' export const REQUIRED_FIELD_MSG = 'This is a required field' -export const INVALID_VOLUME_MOUNT_PATH_IN_CM_CS = 'Use only alphanumeric, (/), (-), (_); Do not use "spaces"' export const MAX_LENGTH_30 = 'Max 30 characters allowed' export const MAX_LENGTH_350 = 'Max 350 characters allowed' export const REPO_NAME_VALIDATION = 'Repository name is not valid; Invalid character(s) "_"' diff --git a/src/config/constants.ts b/src/config/constants.ts index 7c64030364..a0da23cd13 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -249,7 +249,6 @@ export const Routes = { EDIT: 'edit', JOB_CONFIG_ENVIRONMENTS: 'config/environment', PERMISSION: 'permission/check', - SCOPED_GLOBAL_VARIABLES: 'global/variables', SCOPED_GLOBAL_VARIABLES_DETAIL: 'global/variables/detail', GVK: 'gvk', USER: 'user', @@ -282,11 +281,7 @@ export const PATTERNS = { STRING: /[A-Za-z0-9]+$/, APP_NAME: '^[a-z][a-z0-9-]*[a-z0-9]$/*', CD_PIPELINE_NAME: `^[a-z]+[a-z0-9\-\?]*[a-z0-9]+$`, - CONFIG_MAP_AND_SECRET_KEY: /^[-._a-zA-Z0-9]+$/, - CONFIGMAP_AND_SECRET_NAME: /^[a-z0-9][a-z0-9-.]*[a-z0-9]$/, - ALL_DIGITS_BETWEEN_0_AND_7: /^[0-7]*$/, APP_LABEL_CHIP: /^.+:.+$/, - CONFIG_MAP_AND_SECRET_MULTPLS_KEYS: /^[-._a-zA-Z0-9\,\?\s]*[-._a-zA-Z0-9\s]$/, VARIABLE: /^[A-z0-9-_]+$/, API_TOKEN: '^[a-z0-9][a-z0-9_-]*[a-z0-9]$/*', NAMESPACE: '^[a-z0-9]+([a-z0-9-?]*[a-z0-9])?$', @@ -299,7 +294,6 @@ export const PATTERNS = { START_END_ALPHANUMERIC: /^([A-Za-z0-9]).*[A-Za-z0-9]$|^[A-Za-z0-9]{1}$/, ALPHANUMERIC_WITH_SPECIAL_CHAR: /^[A-Za-z0-9._-]+$/, // allow alphanumeric,(.) ,(-),(_) CUSTOM_TAG: /^(?![.-])([a-zA-Z0-9_.-]*\{[Xx]\}[a-zA-Z0-9_.-]*)(? = { }, } -const FormComponent = ({ validationMode }: { validationMode: 'onChange' | 'onBlur' }) => { +const FormComponent = ({ validationMode }: Pick[0], 'validationMode'>) => { const { data, errors, register, handleSubmit, formState } = useForm({ initialValues: { email: '', password: '' }, validations, @@ -91,3 +91,9 @@ export const FormWithOnBlurValidationMode: Story = { validationMode: 'onBlur', }, } + +export const FormWithAllValidationMode: Story = { + args: { + validationMode: 'all', + }, +} diff --git a/yarn.lock b/yarn.lock index 34af3e3f5e..a96dcf6aaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -981,10 +981,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@devtron-labs/devtron-fe-common-lib@1.5.5": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.5.5.tgz#79c82a77fb0663eeedfa336c16b596393ab6a4e6" - integrity sha512-5Qah0zbPmI7wiOBrYMJV9P43eIblRyIrNQ3A87bkxEjWRpW/arRnUV2F4WvJ3/g45EIECCTDIFxE7a1ignXblg== +"@devtron-labs/devtron-fe-common-lib@1.5.6": + version "1.5.6" + resolved "https://registry.yarnpkg.com/@devtron-labs/devtron-fe-common-lib/-/devtron-fe-common-lib-1.5.6.tgz#321e53059952f78817f6f88d9d99b01c51b1efcc" + integrity sha512-L3VjKOf4YlSC1j/WgPj9ZaWO/47iMgIPJRu93JgD6Gk8wmIJEwl9dH1lMSS/J/r07umDnbxxse01cIGgt6wmNw== dependencies: "@types/react-dates" "^21.8.6" ansi_up "^5.2.1" @@ -996,6 +996,7 @@ nanoid "^3.3.8" react-dates "^21.8.0" react-diff-viewer-continued "^3.4.0" + react-draggable "^4.4.5" react-monaco-editor "^0.54.0" react-virtualized-sticky-tree "^3.0.0-beta18" sass "^1.69.7" @@ -1718,100 +1719,100 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz#14c737dc19603a096568044eadaa60395eefb809" - integrity sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q== - -"@rollup/rollup-android-arm64@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz#9d81ea54fc5650eb4ebbc0a7d84cee331bfa30ad" - integrity sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w== - -"@rollup/rollup-darwin-arm64@4.30.1": - version "4.30.1" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz" - integrity sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q== - -"@rollup/rollup-darwin-x64@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz#0ca99741c3ed096700557a43bb03359450c7857d" - integrity sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA== - -"@rollup/rollup-freebsd-arm64@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz#233f8e4c2f54ad9b719cd9645887dcbd12b38003" - integrity sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ== - -"@rollup/rollup-freebsd-x64@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz#dfba762a023063dc901610722995286df4a48360" - integrity sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw== - -"@rollup/rollup-linux-arm-gnueabihf@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz#b9da54171726266c5ef4237f462a85b3c3cf6ac9" - integrity sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg== - -"@rollup/rollup-linux-arm-musleabihf@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz#b9db69b3f85f5529eb992936d8f411ee6d04297b" - integrity sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug== - -"@rollup/rollup-linux-arm64-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz#2550cf9bb4d47d917fd1ab4af756d7bbc3ee1528" - integrity sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw== - -"@rollup/rollup-linux-arm64-musl@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz#9d06b26d286c7dded6336961a2f83e48330e0c80" - integrity sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA== - -"@rollup/rollup-linux-loongarch64-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz#e957bb8fee0c8021329a34ca8dfa825826ee0e2e" - integrity sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz#e8585075ddfb389222c5aada39ea62d6d2511ccc" - integrity sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw== - -"@rollup/rollup-linux-riscv64-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz#7d0d40cee7946ccaa5a4e19a35c6925444696a9e" - integrity sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw== - -"@rollup/rollup-linux-s390x-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz#c2dcd8a4b08b2f2778eceb7a5a5dfde6240ebdea" - integrity sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA== - -"@rollup/rollup-linux-x64-gnu@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz#183637d91456877cb83d0a0315eb4788573aa588" - integrity sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg== - -"@rollup/rollup-linux-x64-musl@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz#036a4c860662519f1f9453807547fd2a11d5bb01" - integrity sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow== - -"@rollup/rollup-win32-arm64-msvc@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz#51cad812456e616bfe4db5238fb9c7497e042a52" - integrity sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw== - -"@rollup/rollup-win32-ia32-msvc@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz#661c8b3e4cd60f51deaa39d153aac4566e748e5e" - integrity sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw== - -"@rollup/rollup-win32-x64-msvc@4.30.1": - version "4.30.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz#73bf1885ff052b82fbb0f82f8671f73c36e9137c" - integrity sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og== +"@rollup/rollup-android-arm-eabi@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz#d4dd60da0075a6ce9a6c76d71b8204f3e1822285" + integrity sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA== + +"@rollup/rollup-android-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz#25c4d33259a7a2ccd2f52a5ffcc0bb3ab3f0729d" + integrity sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g== + +"@rollup/rollup-darwin-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz" + integrity sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g== + +"@rollup/rollup-darwin-x64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz#58ff20b5dacb797d3adca19f02a21c532f9d55bf" + integrity sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ== + +"@rollup/rollup-freebsd-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz#96ce1a241c591ec3e068f4af765d94eddb24e60c" + integrity sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew== + +"@rollup/rollup-freebsd-x64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz#e59e7ede505be41f0b4311b0b943f8eb44938467" + integrity sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA== + +"@rollup/rollup-linux-arm-gnueabihf@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz#e455ca6e4ff35bd46d62201c153352e717000a7b" + integrity sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw== + +"@rollup/rollup-linux-arm-musleabihf@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz#bc1a93d807d19e70b1e343a5bfea43723bcd6327" + integrity sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg== + +"@rollup/rollup-linux-arm64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz#f38bf843f1dc3d5de680caf31084008846e3efae" + integrity sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA== + +"@rollup/rollup-linux-arm64-musl@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz#b3987a96c18b7287129cf735be2dbf83e94d9d05" + integrity sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g== + +"@rollup/rollup-linux-loongarch64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz#0f0324044e71c4f02e9f49e7ec4e347b655b34ee" + integrity sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz#809479f27f1fd5b4eecd2aa732132ad952d454ba" + integrity sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ== + +"@rollup/rollup-linux-riscv64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz#7bc75c4f22db04d3c972f83431739cfa41c6a36e" + integrity sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw== + +"@rollup/rollup-linux-s390x-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz#cfe8052345c55864d83ae343362cf1912480170e" + integrity sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ== + +"@rollup/rollup-linux-x64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz#c6b048f1e25f3fea5b4bd246232f4d07a159c5a0" + integrity sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g== + +"@rollup/rollup-linux-x64-musl@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz#615273ac52d1a201f4de191cbd3389016a9d7d80" + integrity sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA== + +"@rollup/rollup-win32-arm64-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz#32ed85810c1b831c648eca999d68f01255b30691" + integrity sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw== + +"@rollup/rollup-win32-ia32-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz#d47effada68bcbfdccd30c4a788d42e4542ff4d3" + integrity sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ== + +"@rollup/rollup-win32-x64-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz#7a2d89a82cf0388d60304964217dd7beac6de645" + integrity sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw== "@sentry-internal/feedback@7.119.1": version "7.119.1" @@ -2537,7 +2538,12 @@ dependencies: "@types/d3-color" "*" -"@types/d3-path@*", "@types/d3-path@^1": +"@types/d3-path@*": + version "3.1.0" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz" + integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ== + +"@types/d3-path@^1": version "1.0.11" resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.11.tgz" integrity sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw== @@ -3600,7 +3606,7 @@ cli-truncate@^3.1.0: clsx@^1.0.4, clsx@^1.1.1: version "1.2.1" - resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== clsx@^2.0.0: @@ -6698,7 +6704,7 @@ react-docgen@^7.0.0: react-draggable@^4.4.5: version "4.4.6" - resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.6.tgz#63343ee945770881ca1256a5b6fa5c9f5983fe1e" integrity sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw== dependencies: clsx "^1.1.1" @@ -7162,31 +7168,31 @@ rollup-pluginutils@^2.8.1: estree-walker "^0.6.1" rollup@^2.43.1, rollup@^4.20.0, rollup@^4.22.4: - version "4.30.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.30.1.tgz#d5c3d066055259366cdc3eb6f1d051c5d6afaf74" - integrity sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w== + version "4.31.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.31.0.tgz#b84af969a0292cb047dce2c0ec5413a9457597a4" + integrity sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw== dependencies: "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.30.1" - "@rollup/rollup-android-arm64" "4.30.1" - "@rollup/rollup-darwin-arm64" "4.30.1" - "@rollup/rollup-darwin-x64" "4.30.1" - "@rollup/rollup-freebsd-arm64" "4.30.1" - "@rollup/rollup-freebsd-x64" "4.30.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.30.1" - "@rollup/rollup-linux-arm-musleabihf" "4.30.1" - "@rollup/rollup-linux-arm64-gnu" "4.30.1" - "@rollup/rollup-linux-arm64-musl" "4.30.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.30.1" - "@rollup/rollup-linux-powerpc64le-gnu" "4.30.1" - "@rollup/rollup-linux-riscv64-gnu" "4.30.1" - "@rollup/rollup-linux-s390x-gnu" "4.30.1" - "@rollup/rollup-linux-x64-gnu" "4.30.1" - "@rollup/rollup-linux-x64-musl" "4.30.1" - "@rollup/rollup-win32-arm64-msvc" "4.30.1" - "@rollup/rollup-win32-ia32-msvc" "4.30.1" - "@rollup/rollup-win32-x64-msvc" "4.30.1" + "@rollup/rollup-android-arm-eabi" "4.31.0" + "@rollup/rollup-android-arm64" "4.31.0" + "@rollup/rollup-darwin-arm64" "4.31.0" + "@rollup/rollup-darwin-x64" "4.31.0" + "@rollup/rollup-freebsd-arm64" "4.31.0" + "@rollup/rollup-freebsd-x64" "4.31.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.31.0" + "@rollup/rollup-linux-arm-musleabihf" "4.31.0" + "@rollup/rollup-linux-arm64-gnu" "4.31.0" + "@rollup/rollup-linux-arm64-musl" "4.31.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.31.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.31.0" + "@rollup/rollup-linux-riscv64-gnu" "4.31.0" + "@rollup/rollup-linux-s390x-gnu" "4.31.0" + "@rollup/rollup-linux-x64-gnu" "4.31.0" + "@rollup/rollup-linux-x64-musl" "4.31.0" + "@rollup/rollup-win32-arm64-msvc" "4.31.0" + "@rollup/rollup-win32-ia32-msvc" "4.31.0" + "@rollup/rollup-win32-x64-msvc" "4.31.0" fsevents "~2.3.2" run-parallel@^1.1.9: