Skip to content

Commit

Permalink
feat: add support for ULID validation (#58)
Browse files Browse the repository at this point in the history
* Add support for ULID validation

* Add integration and helper tests
  • Loading branch information
simoneNEMO committed Jun 27, 2024
1 parent 60ff1ad commit 02d3a28
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const messages = {
'notIn': 'The selected {{ field }} is invalid',
'ipAddress': 'The {{ field }} field must be a valid IP address',
'uuid': 'The {{ field }} field must be a valid UUID',
'ulid': 'The {{ field }} field must be a valid ULID',
'hexCode': 'The {{ field }} field must be a valid hex color code',

'boolean': 'The value must be a boolean',
Expand Down
9 changes: 9 additions & 0 deletions src/schema/string/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
urlRule,
jwtRule,
uuidRule,
ulidRule,
trimRule,
ibanRule,
alphaRule,
Expand Down Expand Up @@ -66,6 +67,7 @@ export class VineString extends BaseLiteralType<string, string, string> {
url: urlRule,
iban: ibanRule,
uuid: uuidRule,
ulid: ulidRule,
trim: trimRule,
email: emailRule,
alpha: alphaRule,
Expand Down Expand Up @@ -327,6 +329,13 @@ export class VineString extends BaseLiteralType<string, string, string> {
return this.use(uuidRule(...args))
}

/**
* Validates the value to be a valid ULID
*/
ulid() {
return this.use(ulidRule())
}

/**
* Validates the value contains ASCII characters only
*/
Expand Down
13 changes: 13 additions & 0 deletions src/schema/string/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,19 @@ export const uuidRule = createRule<{ version?: (1 | 2 | 3 | 4 | 5)[] } | undefin
}
)

/**
* Validates the value to be a valid ULID
*/
export const ulidRule = createRule((value, _, field) => {
if (!field.isValid) {
return
}

if (!helpers.isULID(value as string)) {
field.report(messages.ulid, 'ulid', field)
}
})

/**
* Validates the value contains ASCII characters only
*/
Expand Down
19 changes: 19 additions & 0 deletions src/vine/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import type { FieldContext } from '../types.js'
const BOOLEAN_POSITIVES = ['1', 1, 'true', true, 'on']
const BOOLEAN_NEGATIVES = ['0', 0, 'false', false]

const ULID = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/

/**
* Collection of helpers used across the codebase to coerce
* and type-check values from HTML forms.
Expand Down Expand Up @@ -228,6 +230,23 @@ export const helpers = {
'US',
] as const,

/**
* Check if the value is a valid ULID
*/
isULID(value: unknown): boolean {
if (typeof value !== 'string') {
return false
}

// Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'
// https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings
if (value[0] > '7') {
return false
}

return ULID.test(value)
},

/**
* Check if the value is a valid color hexcode
*/
Expand Down
54 changes: 54 additions & 0 deletions tests/integration/schema/string.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* @vinejs/vine
*
* (c) VineJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { test } from '@japa/runner'
import vine from '../../../index.js'

test.group('String', () => {
test('fail when value is not a string', async ({ assert }) => {
const schema = vine.object({
name: vine.string(),
})

const data = { name: 42 }
await assert.validationErrors(vine.validate({ schema, data }), [
{
field: 'name',
message: 'The name field must be a string',
rule: 'string',
},
])
})

test('fail when value is not a valid ULID', async ({ assert }) => {
const schema = vine.object({
id: vine.string().ulid(),
})

const data = { id: '01J0TMIXKWW62H0BKGQ984AS' }
await assert.validationErrors(vine.validate({ schema, data }), [
{
field: 'id',
message: 'The id field must be a valid ULID',
rule: 'ulid',
},
])
})

test('pass when value is a valid ULID', async ({ assert }) => {
const schema = vine.object({
id: vine.string().ulid(),
})

const data = { id: '01J0TMSK8WMJSTX1T2633GFA4G' }
await assert.validationOutput(vine.validate({ schema, data }), {
id: '01J0TMSK8WMJSTX1T2633GFA4G',
})
})
})
6 changes: 6 additions & 0 deletions tests/unit/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,10 @@ test.group('Vine Helpers', () => {
assert.isTrue(vine.helpers.isPostalCode('69200', 'FR'))
assert.isTrue(vine.helpers.isMobilePhone('0612345678', 'fr-FR'))
})

test('check if the value is a ULID', ({ assert }) => {
assert.isTrue(vine.helpers.isULID('01J0TSV6ZP6VTAHWZ7A7SYDBM2'))
assert.isTrue(vine.helpers.isULID('01j0tsv6zp6vtahwz7a7sydbm2'))
assert.isFalse(vine.helpers.isULID('01J0TSV6ZP6VTAHWZIL7A7SYDB'))
})
})
41 changes: 41 additions & 0 deletions tests/unit/rules/string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
passportRule,
postalCodeRule,
uuidRule,
ulidRule,
asciiRule,
ibanRule,
jwtRule,
Expand Down Expand Up @@ -1288,6 +1289,46 @@ test.group('String | uuid', () => {
.run(stringRuleValidator)
})

test.group('String | ulid', () => {
test('validate {value}')
.with([
{
errorsCount: 1,
rule: ulidRule(),
value: 22,
error: 'The dummy field must be a string',
},
{
errorsCount: 1,
rule: ulidRule(),
value: 22,
bail: false,
error: 'The dummy field must be a string',
},
{
errorsCount: 1,
rule: ulidRule(),
value: '1999010301',
error: 'The dummy field must be a valid ULID',
},
{
rule: ulidRule(),
value: '01HZW62CR5FNVW4PSXVXC1HTZF',
},
{
rule: ulidRule(),
value: '7ZZZZZZZZZZZZZZZZZZZZZZZZZ',
},
{
errorsCount: 1,
rule: ulidRule(),
value: '80000000000000000000000000',
error: 'The dummy field must be a valid ULID',
},
])
.run(stringRuleValidator)
})

test.group('String | ascii', () => {
test('validate {value}')
.with([
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/schema/string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
passportRule,
postalCodeRule,
uuidRule,
ulidRule,
asciiRule,
ibanRule,
jwtRule,
Expand Down Expand Up @@ -667,6 +668,11 @@ test.group('VineString | applying rules', () => {
schema: vine.string().uuid(),
rule: uuidRule(),
},
{
name: 'ulid',
schema: vine.string().ulid(),
rule: ulidRule(),
},
{
name: 'ascii',
schema: vine.string().ascii(),
Expand Down

0 comments on commit 02d3a28

Please sign in to comment.