diff --git a/src/components/inputs/PrefixedName/shared.ts b/src/components/inputs/PrefixedName/shared.ts index 906f2fd47..95d3e0966 100644 --- a/src/components/inputs/PrefixedName/shared.ts +++ b/src/components/inputs/PrefixedName/shared.ts @@ -8,7 +8,7 @@ export const validateCatalogName = ( ): PrefixedName_Errors => { const isBlank = !hasLength(value); - // See iff this field is allowed to be blank + // See if this field is allowed to be blank if (!allowBlank && isBlank) { return ['missing']; } diff --git a/src/lang/en-US.ts b/src/lang/en-US.ts index fed065753..517d2fccd 100644 --- a/src/lang/en-US.ts +++ b/src/lang/en-US.ts @@ -1311,6 +1311,7 @@ const PrefixedName: ResolvedIntlConfig['messages'] = { const CustomErrors: ResolvedIntlConfig['messages'] = { 'custom.prefixedName.noAccessGrants': `You do not have the necessary ${CommonMessages['terms.permissions']}. Please contact an administrator.`, 'custom.prefixedName.prefix.missing': `please select an organization`, + 'custom.prefixedName.prefix.invalid': `may only include ${CommonMessages['catalogName.limitations']} separated by forward slashes`, 'custom.prefixedName.name.missing': `please provide a name`, 'custom.prefixedName.name.unclean': `cannot contain ./ or ../`, 'custom.prefixedName.name.endingSlash': `cannot end with /`, diff --git a/src/stores/DetailsForm/Store.ts b/src/stores/DetailsForm/Store.ts index 0503386d2..f5ebc4d91 100644 --- a/src/stores/DetailsForm/Store.ts +++ b/src/stores/DetailsForm/Store.ts @@ -1,6 +1,5 @@ import { getConnectors_detailsForm } from 'api/connectors'; import { getLiveSpecs_detailsForm } from 'api/liveSpecsExt'; -import { validateCatalogName } from 'components/inputs/PrefixedName/shared'; import { GlobalSearchParams } from 'hooks/searchParams/useGlobalSearchParams'; import produce from 'immer'; import { isEmpty, isEqual } from 'lodash'; @@ -19,6 +18,7 @@ import { getStoreWithHydrationSettings, } from 'stores/extensions/Hydration'; import { DetailsFormStoreNames } from 'stores/names'; +import { CATALOG_NAME_PATTERN } from 'utils/misc-utils'; import { devtoolsOptions } from 'utils/store-utils'; import { ConnectorVersionEvaluationOptions, @@ -115,12 +115,16 @@ export const getInitialState = ( // Run validation on the name. This is done inside the input but // having the input set custom errors causes issues as we basically // make two near identical calls to the store and that causes problems. - const nameValidation = validateCatalogName( + const NAME_RE = new RegExp(CATALOG_NAME_PATTERN); + + const nameValidation = NAME_RE.test( state.details.data.entityName - ); + ) + ? null + : ['invalid']; // We only have custom errors to handle name validation so it is okay - // to totally clear out customErrors and not inteligently update it + // to totally clear out customErrors and not intelligently update it // As of Q3 2023 // TODO (intl) need to get a way for this kind of error to be translated // and passed into the store diff --git a/src/utils/misc-utils.ts b/src/utils/misc-utils.ts index d7d7851fe..0146635d5 100644 --- a/src/utils/misc-utils.ts +++ b/src/utils/misc-utils.ts @@ -4,6 +4,7 @@ import { createSearchParams } from 'react-router-dom'; // Based on pattern taken from // https://github.com/estuary/animated-carnival/blob/main/supabase/migrations/03_catalog-types.sql export const PREFIX_NAME_PATTERN = `[a-zA-Z0-9-_.]+`; +export const CATALOG_NAME_PATTERN = `^(${PREFIX_NAME_PATTERN}/)+${PREFIX_NAME_PATTERN}$`; // Based on the patterns connectors use for date time // eslint-disable-next-line no-useless-escape