Skip to content

Commit

Permalink
fix(entities-*): allow UTF-8 chars in some fields (#1282)
Browse files Browse the repository at this point in the history
  • Loading branch information
sumimakito authored Mar 22, 2024
1 parent eb528d0 commit 26da464
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ import {
EntityBaseFormType,
EntityFormSection,
useAxios,
useDebouncedFilter, useErrors,
useDebouncedFilter, useErrors, useValidators,
} from '@kong-ui-public/entities-shared'
import '@kong-ui-public/entities-shared/dist/style.css'
import composables from '../composables'
Expand Down Expand Up @@ -164,6 +164,7 @@ const { axiosInstance } = useAxios({
headers: props.config?.requestHeaders,
})
const { getMessageFromError } = useErrors()
const validators = useValidators()
const displayedConsumers = computed((): MultiselectItem[] => {
return results.value.map((record: Record<string, any>) => ({
Expand Down Expand Up @@ -236,11 +237,7 @@ const updateFormValues = async (data: Record<string, any>): Promise<void> => {
Object.assign(originalFields, state.fields)
}
const preValidateErrorMessage = computed(() => {
const namePattern = /^[0-9a-zA-Z.\-_~]*$/
return namePattern.test(state.fields.name) ? '' : t('consumer_groups.form.validation_errors.name')
})
const preValidateErrorMessage = computed(() => validators.utf8Name(state.fields.name))
const getPayload = computed((): ConsumerGroupPayload => {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@
"label": "Consumers",
"placeholder": "Select one or more consumers"
}
},
"validation_errors": {
"name": "The name can be any string containing letters, numbers, or the following characters: ., -, _, or ~. Do not use spaces."
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,35 @@ describe('<GatewayServiceForm />', { viewportHeight: 800, viewportWidth: 700 },
cy.getTestId('form-submit').should('be.disabled')
})

it("should check for name's validity", () => {
cy.mount(GatewayServiceForm, {
props: {
config: baseConfigKonnect,
},
})

cy.get('.kong-ui-entities-gateway-service-form').should('be.visible')
cy.get('.kong-ui-entities-gateway-service-form form').should('be.visible')

cy.getTestId('gateway-service-name-input').should('be.visible')
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')

cy.getTestId('gateway-service-name-input').type('service')
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')

cy.getTestId('gateway-service-name-input').clear()
cy.getTestId('gateway-service-name-input').type('service abc') // with a space
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.first().find('.help-text').should('be.visible')

cy.getTestId('gateway-service-name-input').clear()
cy.getTestId('gateway-service-name-input').type('Hello-ÆBČÐẼF-你好-妳好-こんにちは-안녕하세요-𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯') // UTF-8
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')
})

it('should enable Save button if Upstream URL is selected and Upstream URL field is filled in', () => {
cy.mount(GatewayServiceForm, {
props: {
Expand Down Expand Up @@ -436,6 +465,35 @@ describe('<GatewayServiceForm />', { viewportHeight: 800, viewportWidth: 700 },
cy.getTestId('form-submit').should('be.disabled')
})

it("should check for name's validity", () => {
cy.mount(GatewayServiceForm, {
props: {
config: baseConfigKonnect,
},
})

cy.get('.kong-ui-entities-gateway-service-form').should('be.visible')
cy.get('.kong-ui-entities-gateway-service-form form').should('be.visible')

cy.getTestId('gateway-service-name-input').should('be.visible')
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')

cy.getTestId('gateway-service-name-input').type('service')
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')

cy.getTestId('gateway-service-name-input').clear()
cy.getTestId('gateway-service-name-input').type('service abc') // with a space
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.first().find('.help-text').should('be.visible')

cy.getTestId('gateway-service-name-input').clear()
cy.getTestId('gateway-service-name-input').type('Hello-ÆBČÐẼF-你好-妳好-こんにちは-안녕하세요-𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯') // UTF-8
cy.getTestId('gateway-service-name-input').parents('.k-input-wrapper.input-error')
.should('not.exist')
})

it('should enable Save button if Upstream URL is selected and Upstream URL field is filled in', () => {
cy.mount(GatewayServiceForm, {
props: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:placeholder="t('gateway_services.form.fields.name.placeholder')"
:readonly="form.isReadonly"
type="text"
@input="preValidate"
@input="validateName"
/>

<KInput
Expand Down Expand Up @@ -361,6 +361,7 @@ import {
EntityFormSection,
EntityBaseForm,
EntityBaseFormType,
useValidators,
} from '@kong-ui-public/entities-shared'
import '@kong-ui-public/entities-shared/dist/style.css'
Expand Down Expand Up @@ -408,6 +409,7 @@ const { getMessageFromError } = useErrors()
const { axiosInstance } = useAxios({
headers: props.config?.requestHeaders,
})
const validators = useValidators()
const fetchUrl = computed<string>(() => endpoints.form[props.config.app].edit)
const formType = computed((): EntityBaseFormType => props.gatewayServiceId ? EntityBaseFormType.Edit : EntityBaseFormType.Create)
Expand Down Expand Up @@ -621,21 +623,8 @@ const showTlsVerify = computed((): boolean => {
return checkedGroup.value === 'protocol' && isValidProtocol
})
/**
* Check whether the given string matches name formats
* and returns an error message (if invalid) or empty string (if valid)
* @param {String} str the str to test
* @returns {String} an error message
*/
const validateName = (str: string): string => {
// eslint-disable-next-line prefer-regex-literals
const namePattern = new RegExp('^[0-9a-zA-Z.\\-_~]*$')
return namePattern.test(str) ? '' : t('errors.validationNameError')
}
const preValidate = (input: string): void => {
preValidateErrorMessage.value = validateName(input)
const validateName = (input: string): void => {
preValidateErrorMessage.value = validators.utf8Name(input)
}
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@
"general": "Gateway Services could not be retrieved",
"delete": "The gateway service could not be deleted at this time.",
"copy": "Failed to copy to clipboard",
"urlErrorMessage": "Error: invalid URL",
"validationNameError": "The name can be any string containing letters, numbers, or the following characters: ., -, _, or ~. Do not use spaces."
"urlErrorMessage": "Error: invalid URL"
},
"copy": {
"success": "Copied {val} to clipboard",
Expand Down
2 changes: 2 additions & 0 deletions packages/entities/entities-shared/src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useStringHelpers from './useStringHelpers'
import useI18n from './useI18n'
import useGatewayFeatureSupported from './useGatewayFeatureSupported'
import useTruncationDetector from './useTruncationDetector'
import useValidators from './useValidators'

// All composables must be exported as part of the default object for Cypress test stubs
export default {
Expand All @@ -25,4 +26,5 @@ export default {
useI18n,
useGatewayFeatureSupported,
useTruncationDetector,
useValidators,
}
16 changes: 16 additions & 0 deletions packages/entities/entities-shared/src/composables/useValidators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useI18n from './useI18n'

/**
* Provides a collection of validator functions for entity fields.
* Each validator function returns an error message if the field is invalid, or an empty string if it is valid.
*/
export default function useValidators() {
const { i18n: { t } } = useI18n()

const validateUTF8Name = (name:string) =>
/^[\p{N}\p{L}.\-_~]*$/u.test(name) ? '' : t('validationErrors.utf8Name')

return {
utf8Name: validateUTF8Name,
}
}
4 changes: 2 additions & 2 deletions packages/entities/entities-shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import YamlCodeBlock from './components/common/YamlCodeBlock.vue'
import composables from './composables'

// Extract specific composables to export
const { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector } = composables
const { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators } = composables

// Components
export { EntityBaseConfigCard, ConfigCardItem, ConfigCardDisplay, InternalLinkItem, EntityBaseForm, EntityBaseTable, EntityDeleteModal, EntityFilter, EntityToggleModal, PermissionsWrapper, EntityFormSection, EntityLink, JsonCodeBlock, YamlCodeBlock }

// Composables
export { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector }
export { useAxios, useDeleteUrlBuilder, useErrors, useExternalLinkCreator, useFetchUrlBuilder, useFetcher, useDebouncedFilter, useStringHelpers, useHelpers, useGatewayFeatureSupported, useTruncationDetector, useValidators }

// Types
export * from './types'
3 changes: 3 additions & 0 deletions packages/entities/entities-shared/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
"unexpected": "An unexpected error has occurred",
"dataKeyUndefined": "The data key \"{dataKey}\" does not exist in the response."
},
"validationErrors": {
"utf8Name": "The name can be any string containing characters, letters, numbers, or the following characters: ., -, _, or ~. Do not use spaces."
},
"toggleModal": {
"enable": {
"title": "Enable {entityType}",
Expand Down

0 comments on commit 26da464

Please sign in to comment.