Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7482/validators exclude include caseinsensitive #7573

Merged
2 changes: 2 additions & 0 deletions docs/docs/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ validate(input.name, 'Name', {
##### Options

* `in`: the list of values that cannot be used
* `caseSensitive`: toggles case sensitivity; default: `true`

```jsx
validate(input.name, 'Name', {
Expand Down Expand Up @@ -339,6 +340,7 @@ validate(input.role, 'Role', {
##### Options

* `in`: the list of values that can be used
* `caseSensitive`: toggles case sensitivity; default: `true`

```jsx
validate(input.role, 'Role', {
Expand Down
58 changes: 58 additions & 0 deletions packages/api/src/validations/__tests__/validations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,42 @@ describe('validate exclusion', () => {
expect(() =>
validate('bar', 'selection', { exclusion: { in: ['foo', 'bar'] } })
).toThrow(ValidationErrors.ExclusionValidationError)
expect(() =>
validate('bar', 'selection', {
exclusion: { in: ['foo', 'bar'], caseSensitive: true },
})
).toThrow(ValidationErrors.ExclusionValidationError)

expect(() =>
validate('qux', 'selection', { exclusion: ['foo', 'bar'] })
).not.toThrow()
expect(() =>
validate('qux', 'selection', { exclusion: { in: ['foo', 'bar'] } })
).not.toThrow()
expect(() =>
validate('qux', 'selection', {
exclusion: { in: ['foo', 'bar'], caseSensitive: true },
})
).not.toThrow()
})

it('checks for case-insensitive exclusion', () => {
expect(() =>
validate('Bar', 'selection', {
exclusion: { in: ['foo', 'bar'], caseSensitive: false },
})
).toThrow(ValidationErrors.ExclusionValidationError)
expect(() =>
validate('bar', 'selection', {
exclusion: { in: ['foo', 'Bar'], caseSensitive: false },
})
).toThrow(ValidationErrors.ExclusionValidationError)

expect(() =>
validate('qux', 'selection', {
exclusion: { in: ['foo', 'bar'], caseSensitive: false },
})
).not.toThrow()
})

it('throws with a default message', () => {
Expand Down Expand Up @@ -325,13 +354,42 @@ describe('validate inclusion', () => {
expect(() =>
validate('quux', 'selection', { inclusion: { in: ['foo', 'bar'] } })
).toThrow(ValidationErrors.InclusionValidationError)
expect(() =>
validate('QUUX', 'selection', {
inclusion: { in: ['foo', 'bar'], caseSensitive: true },
})
).toThrow(ValidationErrors.InclusionValidationError)

expect(() =>
validate('foo', 'selection', { inclusion: ['foo', 'bar'] })
).not.toThrow()
expect(() =>
validate('foo', 'selection', { inclusion: { in: ['foo', 'bar'] } })
).not.toThrow()
expect(() =>
validate('foo', 'selection', {
inclusion: { in: ['foo', 'bar'], caseSensitive: true },
})
).not.toThrow()
})

it('checks for case-insensitive inclusion', () => {
expect(() =>
validate('quux', 'selection', {
inclusion: { in: ['foo', 'bar'], caseSensitive: false },
})
).toThrow(ValidationErrors.InclusionValidationError)

expect(() =>
validate('Foo', 'selection', {
inclusion: { in: ['foo', 'bar'], caseSensitive: false },
})
).not.toThrow()
expect(() =>
validate('foo', 'selection', {
inclusion: { in: ['FOO', 'bar'], caseSensitive: false },
})
).not.toThrow()
})

it('throws with a default message', () => {
Expand Down
34 changes: 28 additions & 6 deletions packages/api/src/validations/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface ExclusionValidatorOptions extends WithOptionalMessage {
* The list of values that cannot be used.
*/
in?: Array<unknown>
caseSensitive?: boolean
}

interface FormatValidatorOptions extends WithOptionalMessage {
Expand All @@ -49,6 +50,7 @@ interface InclusionValidatorOptions extends WithOptionalMessage {
* The list of values that can be used.
*/
in?: Array<unknown>
caseSensitive?: boolean
}

interface LengthValidatorOptions extends WithOptionalMessage {
Expand Down Expand Up @@ -309,10 +311,9 @@ const VALIDATORS = {
name: string,
options: Array<unknown> | ExclusionValidatorOptions
) => {
const exclusionList =
(Array.isArray(options) && options) || options.in || []
const [exclusionList, val] = prepareExclusionInclusion(value, options)

if (exclusionList.includes(value)) {
if (exclusionList.includes(val)) {
validationError('exclusion', name, options)
}
},
Expand Down Expand Up @@ -349,10 +350,9 @@ const VALIDATORS = {
name: string,
options: Array<unknown> | InclusionValidatorOptions
) => {
const inclusionList =
(Array.isArray(options) && options) || options.in || []
const [inclusionList, val] = prepareExclusionInclusion(value, options)

if (!inclusionList.includes(value)) {
if (!inclusionList.includes(val)) {
validationError('inclusion', name, options)
}
},
Expand Down Expand Up @@ -549,6 +549,28 @@ const validationError = (
throw new ErrorClass(name, errorMessage, substitutions)
}

// Generate the final list and value used for exclusion/inclusion by taking
// case-sensitivity into consideration. The returned array and value then
// can simply be used with Array.includes to perform exclusion/inclusion checks.
const prepareExclusionInclusion = (
value: unknown,
options:
| Array<unknown>
| InclusionValidatorOptions
| ExclusionValidatorOptions
): [Array<unknown>, unknown] => {
const inputList = (Array.isArray(options) && options) || options.in || []

// default case sensitivity to true
const caseSensitive = Array.isArray(options)
? true
: options.caseSensitive ?? true

return caseSensitive
? [inputList, value]
: [inputList.map((s) => s.toLowerCase()), (value as string).toLowerCase()]
}

// Main validation function, `directives` decides which actual validators
// above to use
//
Expand Down