api-doc-validator
Annotations
Parameters of annotations
Parameter in brackets means it's optional, like [CODE]
. Parameters with pipe sign |
means or
, like json-schema|OBJECT_NAME
.
Usage
@url
@url [METHOD] path
@params
Validate parameters of @url
path
@params [OBJECT_NAME =] json-schema|OBJECT_NAME
or with OBJECT_NAME
assing for future use
or use external schema as root schema
or extend external schema
@query
Validate @url
query parameters
@query [OBJECT_NAME =] json-schema|OBJECT_NAME
Example of valid request GET /users?id=1
Names of fields in @params
and query
should be different to use them in @call
@body
@body [OBJECT_NAME =] json-schema|OBJECT_NAME
@response
Response http code and validation of response body.
@response [CODE] [OBJECT_NAME =] json-schema|OBJECT_NAME
Response for 200
code
Validators for different codes of same request
@namespace
Word used to filter validators in target file. You can use shortcut @ns
Example of generation express middleware with only test
validators
npx adv -c path/to/config.json -n test -e ./test-validator.js
@schema
Define the new schema for future usage
@schema OBJECT_NAME = json-schema|OBJECT_NAME
or just to make shorter schema name
@call
You should provide valid js code of method call. This code will be used in your API tests.
@call object-method-call
METHOD
HTTP request method. Default is config.defaultMethod
GET|POST|PUT|DELETE|HEAD|OPTIONS
CODE
HTTP response code. Default is config.defaultCode
Formats:
200
regular code number2xx
any code between 200 and 299200 - 400
any code between 200 and 400200 || 3xx || 400 - 500
or expression
Example
path
URL pathname
. For path parsing used path-to-regexp lib.
Parameters like :id
can be used in @call
as parameter of method call with same name.
OBJECT_NAME
Any valid js object name like objectName
or with field of any deep objectName.fieldName.field
json-schema
Object that describes how validate another object. For object validation used ajv
lib with few modifications for less code writing.
Default ajv schema
schema = {
type: "object",
properties: {
id: {
type: "number",
},
name: {
type: "string",
},
enabled: {
type: "boolean",
},
list: {
type: "array",
},
user: {
type: "object",
},
enumOfStrings: {
type: "string",
enum: ["user", "guest", "owner"]
},
}
}
Simplified description of schema
schema = {
id: number,
name: string,
enabled: boolean,
listOfObjects: [{
id: number,
type: string,
}],
listOfNumbers: [{
type: "number"
}],
user: {
id: number,
type: string,
},
enumOfStrings: "user" || "guest" || "owner",
}
So, if any object in a schema (including root) has field type
with one of the string values
"number"
, "integer"
, "string"
, "boolean"
, "array"
, "object"
or "null"
than it means this object is validator.
In any other cases this object will be converted to "object"
validator. Example
schema = {
days: [number],
list: [{
id: number,
type: string,
}],
user: {
id: number,
type: string,
},
parent: {
type: "object",
},
}
Will be converted to
schema = {
type: "object",
properties: {
days: {
type: "array",
items: {
type: "number"
}
},
list: {
type: "array",
items: {
type: "object",
required: ["id", "type"],
properties: {
id: {
type: "number"
},
type: {
type: "string"
},
}
}
},
user: {
type: "object",
required: ["id", "type"],
properties: {
id: {
type: "number"
},
type: {
type: "string"
},
}
},
parent: {
type: "object",
},
}
}
Optional object fields
By default, all fields in an object are required. To make field optional just put it in brackets.
schema = {
id: number,
[name]: string,
}
schema = {
type: "object",
required: ["id"],
properties: {
id: {type: "number"},
name: {type: "string"},
},
}
Number patterns
Instead of short number
validator you can use one of following number patterns as value of object field.
int
number without floating-pointpositive
positive number including 0
negative
negative number excluding 0
id
number more than 0
schema = {
id: id,
price: positive,
list: [int],
}
Will be converted to
schema = {
type: "object",
properties: {
id: {
type: "number",
minimum: 1,
},
price: {
type: "number",
minimum: 0,
},
list: {
type: "array",
items: {
type: "integer",
}
},
},
}
String patterns
Instead of short string
validator you can use one of following string patterns as value of object field.
date
full-date according to RFC3339.time
time with optional time-zone.date-time
date-time from the same source (time-zone is optional, in ajv it's mandatory)date-time-tz
date-time with time-zone requireduri
full URI.uri-reference
URI reference, including full and relative URIs.uri-template
URI template according to RFC6570email
email address.hostname
host name according to RFC1034.filename
name (words with dashes) with extensionipv4
IP address v4.ipv6
IP address v6.regex
tests whether a string is a valid regular expression by passing it to RegExp constructor.uuid
Universally Unique Identifier according to RFC4122.
schema = {
id: uuid,
email: email,
created_at: date-time,
days: [date],
}
Will be converted to
schema = {
type: "object",
properties: {
id: {
type: "string",
format: "uuid",
},
email: {
type: "string",
format: "email",
},
created_at: {
type: "string",
format: "date-time",
},
days: {
type: "array",
items: {
type: "string",
format: "date",
}
},
}
}
Inject external schema
Using OBJECT_NAME
you can inject external schema in current schema.
anyOf schema
Instead of anyOf
you can use ||
operator
schema = {
data: User || Account || {type: "object"}
}
will be
schema = {
type: "object",
properties: {
data: {
anyOf: [
{},
{},
{type: "object"},
]
}
}
}
allOf schema
Instead of allOf
you can use &&
operator
schema = {
data: User && Account && {type: "object"}
}
will be
schema = {
type: "object",
properties: {
data: {
allOf: [
{},
{},
{type: "object"},
]
}
}
}
Extend schema
To extend you can use object spread operator
User = {
id: number,
data: string,
}
UserExtra = {
name: string,
created_at: date,
}
schema = {
user: {
...User,
...UserExtra,
data: undefined,
created_at: date-time,
},
}
will be
schema = {
type: "object",
properties: {
user: {
type: "object",
properties: {
id: {type: "number"},
name: {type: "string"},
created_at: {type: "string", format: "date-time"},
}
},
},
}
object-method-call
Uniq sample of JavaScript code with some method call of some object, which will be generated for testing purposes.
In method call you can use named parameters from @url
There can be any number of nested objects and any method name. Only names of parameters should be equal.
CLI
with npx
npx adv -c path/to/config.json
Parameters:
-c, --config <path> path to config json file
-e, --express <path> generate express middleware validator
-n, --namespace <namespace> generate validators only with this namespace or comma separated namespaces
-M, --default-method <method> default @url METHOD
-C, --default-code <code> default @response CODE
--help display help for command
Express middleware
Generate the middleware with npx adv -c path/to/config.json -e path/to/your/app/validator.js
Install in to your project packages ajv, ajv-formats and path-to-regexp. Middleware depends on them.
Then add middleware to your express app
const validator = require('./path/to/your/app/validator.js');
app.use(validator);
app.post('...', (req, res) => {});
app.use(function (err, req, res, next) {
if (err instanceof validator.RequestValidationError) {
console.log(err.message);
console.log(err.property);
console.log(err.errors);
}
else if (err instanceof validator.ResponseValidationError) {
console.log(err.message);
console.log(err.errors);
}
else {
next(err);
}
if (err instanceof validator.ValidationError) {
console.log(err.message);
console.log(err.errors);
}
else {
next(err);
}
});
By default validator
will create ajv
instance like this
const Ajv = require('ajv').default;
const ajv = new Ajv({coerceTypes: true});
require('ajv-formats')(ajv);
validator.ajv = ajv;
But you can overwrite it with your ajv
instance at any time. See Ajv options
const validator = require('./validator.js');
validator.ajv = new Ajv(options);
Universal middleware
Generate the middleware with npx adv -c path/to/config.json -e path/to/your/app/validator.js
Then add it to your app
const validator = require('./validator.js');
function sendMessage(path, data) {
try {
var validateResponse = validator({
url: path,
body: data,
});
}
catch (err) {
console.log(err.message);
console.log(err.property);
console.log(err.errors);
}
return ajax(path, data).then(function (result) {
if (validateResponse) {
try {
validateResponse(result);
}
catch (err) {
console.log(err.message);
console.log(err.errors);
}
}
return result;
});
}
Config
include
array of paths to files relative to config path, glob pattern usedexclude
array of paths to files to be excludeddefaultMethod
overwrites default METHOD. Default is GET
defaultCode
overwrites default CODE. Default is 200
express
same as --express <path>
namespace
same as --namespace <path>
All paths are relative to config file location.
Example
{
"include": [
"src/**/*.js"
],
"exclude": [
"src/tests"
],
"defaultMethod": "POST",
"defaultCode": "2xx || 301"
}