ajv-moment
an ajv plugin using moment for robust date validation in your json-schemas.
Installing
npm install --save ajv moment ajv-moment
Getting Started
import Ajv from 'ajv'
import { plugin } from 'ajv-moment'
import moment from 'moment'
// define an ajv instance
const ajv = new Ajv();
// install the plugin. by default, this plugin utilizes the "moment" custom keyword
plugin({ ajv, moment });
// define your schemas using the "moment" keyword
// here we define a schema for an object with two keys (assigned & due)
// each of which must be a valid date string in ISO 8601 format.
// in addition, we specify that the due key must be greater than or
// equal to the assigned date plus 3 days.
const schema = {
type: 'object',
properties: {
assigned: {
type: 'string',
moment: true
},
due: {
type: 'string',
moment: {
validate: [{
test: 'isSameOrAfter',
value: {
$data: '1/assigned',
manipulate: [{
add: [3, 'days']
}]
}
}]
}
}
},
required: [
'assigned', 'due'
]
}
// compile your schema using ajv
const validate = ajv.compile(schema);
validate({
assigned: new Date().toISOString(),
due: moment().add(24, 'hours').toISOString()
});
// false
validate({
assigned: new Date().toISOString(),
due: moment().add(1, 'weeks').toISOString()
});
// true
Schemas
The custom keyword schema definitions can take on many forms. The simplest being the following:
{
"type": "string",
"description": "a valid date string in ISO 8601 format",
"moment": true
}
This schema will simply enforce that the provided value is a valid date string in ISO 8601 format.
To specify a custom format, a format
option can be defined in the schema definition. When a custom format is specified, moment's strict
parsing option will be enforced.
{
"type": "string",
"description": "a valid date string in MM-DD-YYYY format",
"moment": {
"format": ["MM-DD-YYYY"]
}
}
Aside from date string formats, this plugin can also perform additional validations using moment
. The validations are defined in the validate
subschema keyword and can be used to enforce relative constraints on date strings. See below for some examples.
[
{
"type": "string",
"description": "a valid date string prior to January 1st, 2000 UTC",
"moment": {
"validate": [
{
"test": "isBefore",
"value": "2000-01-01T00:00:00.000Z"
}
]
}
},
{
"type": "object",
"properties": {
"assigned": {
"type": "string",
"description": "a valid date string in ISO 8601 format",
"moment": true
},
"due": {
"type": "string",
"description": "a valid date string in ISO 8601 format that is greater than or equal to the assigned date plus 3 days & 30 minutes",
"moment": {
"validate": [
{
"test": "isSameOrAfter",
"value": {
"$data": "1/assigned",
"manipulate": [
{
"add": [3, "days"]
},
{
"add": [30, "minutes"]
}
]
}
}
]
}
}
},
"required": [
"assigned",
"due"
]
},
{
"type": "object",
"properties": {
"first": {
"type": "string",
"description": "a valid date string in ISO 8601 format",
"moment": true
},
"second": {
"type": "string",
"description": "a valid date string in ISO 8601 format that is greater than the 'first' attribute by at least 30 minutes",
"moment": {
"validate": [
{
"test": "isSameOrAfter",
"value": {
"$data": "1/first",
"manipulate": [
{
"add": [30, "minutes"]
}
]
}
}
]
}
},
"breakpoint": {
"type": "string",
"description": "a valid date string in ISO 8601 format that is both between 'first' and 'second' and also prior to 'second' by at least 15 seconds",
"moment": {
"validate": [
{
"test": "isBetween",
"value": [
{
"$data": "1/first"
},
{
"$data": "1/second"
}
]
},
{
"test": "isSameOrBefore",
"value": {
"$data": "1/second",
"manipulate": [
{
"subtract": [15, "seconds"]
}
]
}
}
]
}
},
"third": {
"type": "string",
"description": "a valid date string prior in ISO 8601 format that after 'second'",
"moment": {
"validate": [
{
"test": "isAfter",
"value": {
"$data": "1/second"
}
}
]
}
}
},
"required": [
"first",
"second",
"breakpoint",
"third"
]
}
]
Pass Test Arguments
The following schema shows how to pass format and testArguments to perform comparison (e.g. isSame(d, 'year')
)
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://some-url/mini-schema.json",
"type": "object",
"title": "Mini Schema for same year - testArgs",
"required": [
"year"
],
"additionalProperties": false,
"properties": {
"year": {
"$id": "#/properties/year",
"type": "string",
"title": "Year (4 digits)",
"description": "a valid year string in YYYY format",
"default": "",
"moment": {
"format": [
"YYYY"
],
"validate": [
{
"test": "isAfter",
"value": "1900",
"format": [
"YYYY"
]
},
{
"test": "isBefore",
"value": "2999",
"format": [
"YYYY"
]
}
]
},
"examples": [
"2020"
]
},
"myOtherDate": {
"$id": "#/properties/myOtherDate",
"type": "string",
"format": "date",
"title": "Other date that has to match the year",
"default": "",
"examples": [
"2020-05-18"
],
"moment": {
"validate": [
{
"test": "isSame",
"value": {
"$data": "1/year"
},
"format": [
"YYYY"
],
"testArgs": [
"year"
]
}
]
}
}
}
}
Try evaluating against the following data
// the error text will be: "isSame(value, ,"year")" validation failed for value(s): 2029-02-18 vs ("2019)"
const invalidData = {
year: '2019',
myOtherDate: '2029-02-18'
};
const validData = {
year: '2019',
myOtherDate: '2019-02-18'
};
Important undefined
or null
values for validate.value.$data
will make the test pass, i.e. when comparing against an optional/nullable dependency, the test will pass. It will be performed only if the referenced value is a date string, or now.
Testing
run all tests
npm test
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
License
Copyright (c) 2016 Chris Ludden. Licensed under the MIT license.