Skip to content

Commit

Permalink
7482/validators exclude include caseinsensitive (#7573)
Browse files Browse the repository at this point in the history
* fixed exclusion and inclusion types to accept Array

* fix 'format' validator types to accept RegExp as expected in unit tests

* remove excess parens to make sure both regexp test cases are run

* validations: add 'caseSensitive' boolean option to exclusion and inclusion

* incluse caseSensitive option in service validation docs
  • Loading branch information
taivo authored and jtoar committed Feb 16, 2023
1 parent 6b54c0d commit f54cb84
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 6 deletions.
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

0 comments on commit f54cb84

Please sign in to comment.