Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit d51a7d3

Browse files
MaximeMRFRomainLanz
authored andcommitted
feat: add bigint type validation
1 parent e15605d commit d51a7d3

6 files changed

Lines changed: 344 additions & 0 deletions

File tree

adonis-typings/validator.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,28 @@ declare module '@ioc:Adonis/Core/Validator' {
388388
}
389389
}
390390

391+
/**
392+
* Signature to define a bigint or bigint number type
393+
*/
394+
export interface BigIntType {
395+
(rules?: Rule[]): {
396+
t: bigint
397+
getTree(): SchemaLiteral
398+
}
399+
optional(rules?: Rule[]): {
400+
t?: bigint
401+
getTree(): SchemaLiteral
402+
}
403+
nullable(rules?: Rule[]): {
404+
t: bigint | null
405+
getTree(): SchemaLiteral
406+
}
407+
nullableAndOptional(rules?: Rule[]): {
408+
t?: bigint | null
409+
getTree(): SchemaLiteral
410+
}
411+
}
412+
391413
/**
392414
* Signature to define an object with members or an optional object
393415
* with members.
@@ -640,6 +662,7 @@ declare module '@ioc:Adonis/Core/Validator' {
640662
string: StringType
641663
boolean: BooleanType
642664
number: NumberType
665+
bigint: BigIntType
643666
date: DateType
644667
enum: EnumType
645668
enumSet: EnumSetType

src/Schema/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
StringType,
2121
ObjectType,
2222
NumberType,
23+
BigIntType,
2324
EnumSetType,
2425
BooleanType,
2526
TypedSchema,
@@ -117,6 +118,28 @@ number.nullableAndOptional = function nullableAndOptionalNumber(rules?: Rule[])
117118
>
118119
}
119120

121+
/**
122+
* bigint schema type
123+
*/
124+
function bigint(rules?: Rule[]) {
125+
return getLiteralType('bigint', false, false, undefined, rules || []) as ReturnType<BigIntType>
126+
}
127+
bigint.optional = function optionalBigInt(rules?: Rule[]) {
128+
return getLiteralType('bigint', true, false, undefined, rules || []) as ReturnType<
129+
BigIntType['optional']
130+
>
131+
}
132+
bigint.nullable = function nullableBigInt(rules?: Rule[]) {
133+
return getLiteralType('bigint', false, true, undefined, rules || []) as ReturnType<
134+
BigIntType['nullable']
135+
>
136+
}
137+
bigint.nullableAndOptional = function nullableAndOptionalBigInt(rules?: Rule[]) {
138+
return getLiteralType('bigint', true, true, undefined, rules || []) as ReturnType<
139+
BigIntType['nullableAndOptional']
140+
>
141+
}
142+
120143
/**
121144
* Date schema type
122145
*/
@@ -359,6 +382,7 @@ export const schema: Schema = {
359382
string,
360383
boolean,
361384
number,
385+
bigint,
362386
date,
363387
object,
364388
array,

src/Validations/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export { oneOf as enum } from './primitives/enum'
4141
export { enumSet } from './primitives/enumSet'
4242
export { file } from './primitives/file'
4343
export { number } from './primitives/number'
44+
export { bigint } from './primitives/bigint'
4445
export { object } from './primitives/object'
4546
export { string } from './primitives/string'
4647

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* @adonisjs/validator
3+
*
4+
* (c) Harminder Virk <virk@adonisjs.com>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { SyncValidation } from '@ioc:Adonis/Core/Validator'
11+
import { wrapCompile } from '../../Validator/helpers'
12+
13+
const DEFAULT_MESSAGE = 'bigint validation failed'
14+
const RULE_NAME = 'bigint'
15+
16+
/**
17+
* Ensure the value is a valid bigint. Numeric string will be casted
18+
* to valid bigint
19+
*/
20+
export const bigint: SyncValidation = {
21+
compile: wrapCompile(RULE_NAME),
22+
validate(value, _, { mutate, errorReporter, pointer, arrayExpressionPointer }) {
23+
if (typeof value === 'bigint') {
24+
return
25+
}
26+
27+
/**
28+
* Report error when value is not a bigint and neither a string
29+
*/
30+
if (typeof value !== 'string') {
31+
errorReporter.report(pointer, RULE_NAME, DEFAULT_MESSAGE, arrayExpressionPointer)
32+
return
33+
}
34+
35+
/**
36+
* Attempt to cast bigint like string to a bigint. In case of
37+
* failure report the validation error
38+
*/
39+
const castedValue = Number(value)
40+
if (isNaN(castedValue)) {
41+
errorReporter.report(pointer, RULE_NAME, DEFAULT_MESSAGE, arrayExpressionPointer)
42+
return
43+
}
44+
45+
if (castedValue === Infinity || castedValue === -Infinity) {
46+
errorReporter.report(pointer, RULE_NAME, DEFAULT_MESSAGE, arrayExpressionPointer)
47+
return
48+
}
49+
50+
/**
51+
* Mutate the value
52+
*/
53+
mutate(castedValue)
54+
},
55+
}

test/schema.spec.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,116 @@ test.group('Schema | Number', () => {
475475
})
476476
})
477477

478+
test.group('Schema | BigInt', () => {
479+
test('define schema with bigint rule', ({ assert }) => {
480+
assert.deepEqual(
481+
schema.create({
482+
username: schema.bigint(),
483+
}).tree,
484+
{
485+
username: {
486+
type: 'literal',
487+
subtype: 'bigint',
488+
optional: false,
489+
nullable: false,
490+
rules: [
491+
{
492+
name: 'required',
493+
allowUndefineds: true,
494+
async: false,
495+
compiledOptions: [],
496+
},
497+
{
498+
name: 'bigint',
499+
allowUndefineds: false,
500+
async: false,
501+
compiledOptions: [],
502+
},
503+
],
504+
},
505+
}
506+
)
507+
})
508+
509+
test('define schema with optional bigint rule', ({ assert }) => {
510+
assert.deepEqual(
511+
schema.create({
512+
username: schema.bigint.optional(),
513+
}).tree,
514+
{
515+
username: {
516+
type: 'literal',
517+
subtype: 'bigint',
518+
optional: true,
519+
nullable: false,
520+
rules: [
521+
{
522+
name: 'bigint',
523+
allowUndefineds: false,
524+
async: false,
525+
compiledOptions: [],
526+
},
527+
],
528+
},
529+
}
530+
)
531+
})
532+
533+
test('define schema with nullable bigint rule', ({ assert }) => {
534+
assert.deepEqual(
535+
schema.create({
536+
username: schema.bigint.nullable(),
537+
}).tree,
538+
{
539+
username: {
540+
type: 'literal',
541+
subtype: 'bigint',
542+
optional: false,
543+
nullable: true,
544+
rules: [
545+
{
546+
name: 'nullable',
547+
allowUndefineds: true,
548+
async: false,
549+
compiledOptions: [],
550+
},
551+
{
552+
name: 'bigint',
553+
allowUndefineds: false,
554+
async: false,
555+
compiledOptions: [],
556+
},
557+
],
558+
},
559+
}
560+
)
561+
})
562+
563+
test('define schema with both optional and nullable bigint rule', ({ assert }) => {
564+
assert.deepEqual(
565+
schema.create({
566+
username: schema.bigint.nullableAndOptional(),
567+
}).tree,
568+
{
569+
username: {
570+
type: 'literal',
571+
subtype: 'bigint',
572+
optional: true,
573+
nullable: true,
574+
rules: [
575+
{
576+
name: 'bigint',
577+
allowUndefineds: false,
578+
async: false,
579+
compiledOptions: [],
580+
},
581+
],
582+
},
583+
}
584+
)
585+
})
586+
})
587+
478588
test.group('Schema | Date', () => {
479589
test('define schema with date rule', ({ assert }) => {
480590
assert.deepEqual(

test/validations/bigint.spec.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* @adonisjs/validator
3+
*
4+
* (c) Harminder Virk <virk@adonisjs.com>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { test } from '@japa/runner'
11+
import { rules } from '../../src/Rules'
12+
import { validate } from '../fixtures/rules/index'
13+
import { MessagesBag } from '../../src/MessagesBag'
14+
import { ApiErrorReporter } from '../../src/ErrorReporter'
15+
import { bigint } from '../../src/Validations/primitives/bigint'
16+
17+
function compile() {
18+
return bigint.compile('literal', 'bigint', rules['bigint']().options, {})
19+
}
20+
21+
test.group('BigInt', () => {
22+
validate(bigint, test, 'helloworld', 10n, compile())
23+
24+
test('report error when value is near Infinity', ({ assert }) => {
25+
const reporter = new ApiErrorReporter(new MessagesBag({}), false)
26+
bigint.validate(
27+
'-3177777777777777777777777777777777777777777777777777777777777777777777777770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999991111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
28+
compile().compiledOptions,
29+
{
30+
errorReporter: reporter,
31+
field: 'age',
32+
pointer: 'age',
33+
tip: {},
34+
root: {},
35+
refs: {},
36+
mutate: () => {},
37+
}
38+
)
39+
40+
assert.deepEqual(reporter.toJSON(), {
41+
errors: [
42+
{
43+
field: 'age',
44+
rule: 'bigint',
45+
message: 'bigint validation failed',
46+
},
47+
],
48+
})
49+
})
50+
51+
test('report error when value is not a valid bigint', ({ assert }) => {
52+
const reporter = new ApiErrorReporter(new MessagesBag({}), false)
53+
bigint.validate(null, compile().compiledOptions, {
54+
errorReporter: reporter,
55+
field: 'age',
56+
pointer: 'age',
57+
tip: {},
58+
root: {},
59+
refs: {},
60+
mutate: () => {},
61+
})
62+
63+
assert.deepEqual(reporter.toJSON(), {
64+
errors: [
65+
{
66+
field: 'age',
67+
rule: 'bigint',
68+
message: 'bigint validation failed',
69+
},
70+
],
71+
})
72+
})
73+
74+
test('cast bigint like string to a valid bigint', ({ assert }) => {
75+
const reporter = new ApiErrorReporter(new MessagesBag({}), false)
76+
let value: any = '21'
77+
78+
bigint.validate(value, compile().compiledOptions, {
79+
errorReporter: reporter,
80+
field: 'age',
81+
pointer: 'age',
82+
tip: {},
83+
root: {},
84+
refs: {},
85+
mutate: (newValue) => {
86+
value = newValue
87+
},
88+
})
89+
90+
assert.deepEqual(reporter.toJSON(), { errors: [] })
91+
assert.equal(value, 21n)
92+
})
93+
94+
test('work fine when value is a valid bigint', ({ assert }) => {
95+
const reporter = new ApiErrorReporter(new MessagesBag({}), false)
96+
bigint.validate(21n, compile().compiledOptions, {
97+
errorReporter: reporter,
98+
field: 'age',
99+
pointer: 'age',
100+
tip: {},
101+
root: {},
102+
refs: {},
103+
mutate: () => {},
104+
})
105+
106+
assert.deepEqual(reporter.toJSON(), { errors: [] })
107+
})
108+
109+
test('report error when value is a string that cannot be casted to a bigint', ({ assert }) => {
110+
const reporter = new ApiErrorReporter(new MessagesBag({}), false)
111+
bigint.validate('hello-world', compile().compiledOptions, {
112+
errorReporter: reporter,
113+
field: 'age',
114+
pointer: 'age',
115+
tip: {},
116+
root: {},
117+
refs: {},
118+
mutate: () => {},
119+
})
120+
121+
assert.deepEqual(reporter.toJSON(), {
122+
errors: [
123+
{
124+
field: 'age',
125+
rule: 'bigint',
126+
message: 'bigint validation failed',
127+
},
128+
],
129+
})
130+
})
131+
})

0 commit comments

Comments
 (0)