UNPKG

2.66 kBPlain TextView Raw
1import { dayjs } from '@naturalcycles/time-lib'
2import { Extension, StringSchema } from 'joi'
3import * as Joi from 'joi'
4import { AnySchemaTyped } from './joi.model'
5
6export interface ExtendedStringSchema extends StringSchema, AnySchemaTyped<string> {
7 dateString(min?: string, max?: string): this
8}
9
10export interface JoiDateStringOptions {
11 min?: string
12 max?: string
13}
14
15export function stringExtensions(joi: typeof Joi): Extension {
16 return {
17 type: 'string',
18 base: joi.string(),
19 messages: {
20 'string.dateString': '"{{#label}}" must be an ISO8601 date (yyyy-mm-dd)',
21 'string.dateStringMin': '"{{#label}}" must be not earlier than {{#min}}',
22 'string.dateStringMax': '"{{#label}}" must be not later than {{#max}}',
23 'string.dateStringCalendarAccuracy': '"{{#label}}" must be a VALID calendar date',
24 'string.stripHTML': '"{{#label}}" must NOT contain any HTML tags',
25 },
26 rules: {
27 dateString: {
28 method(min?: string, max?: string) {
29 return this.$_addRule({
30 name: 'dateString',
31 args: { min, max } as JoiDateStringOptions,
32 })
33 },
34 args: [
35 {
36 name: 'min',
37 ref: true,
38 assert: v => typeof v === 'string',
39 message: 'must be a string',
40 },
41 {
42 name: 'max',
43 ref: true,
44 assert: v => typeof v === 'string',
45 message: 'must be a string',
46 },
47 ],
48 validate(v: string, helpers, args: JoiDateStringOptions) {
49 // console.log('dateString validate called', {v, args})
50
51 let err: string | undefined
52 let { min, max } = args
53
54 // Today allows +-14 hours gap to account for different timezones
55 if (max === 'today') {
56 max = dayjs().add(14, 'hour').toISODate()
57 }
58 if (min === 'today') {
59 min = dayjs().subtract(14, 'hour').toISODate()
60 }
61 // console.log('min/max', min, max)
62
63 const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(v)
64 if (!m || m.length <= 1) {
65 err = 'string.dateString'
66 } else if (min && v < min) {
67 err = 'string.dateStringMin'
68 } else if (max && v > max) {
69 err = 'string.dateStringMax'
70 } else if (!dayjs(v).isValid()) {
71 // todo: replace with another regex (from ajv-validators) for speed
72 err = 'string.dateStringCalendarAccuracy'
73 }
74
75 if (err) {
76 return helpers.error(err, args)
77 }
78
79 return v // validation passed
80 },
81 },
82 },
83 }
84}