Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix some bugs #4

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Node CI

on: [push]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [8.x, 10.x, 12.x]

steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
npm ci
npm run build --if-present
npm test
env:
CI: true
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ language: node_js
node_js:
- '6'
- '8'
before_install:
- npm i npminstall -g
install:
- npm i npminstall && npminstall
- npminstall
script:
- npm run ci
after_script:
Expand Down
165 changes: 102 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,75 @@

The validater based on [ajv](https://github.com/epoberezkin/ajv) for egg.js.

Egg-ajv consider each schema as a small piece, which compose a complete schema by using the key words - [$ref](https://github.com/epoberezkin/ajv#combining-schemas-with-ref), [\$merge and \$patch](https://github.com/epoberezkin/ajv#merge-and-patch-keywords) - to reduce the code repetition.
Egg-ajv consider each schema as a small piece, which compose a complete schema by using the key words - [\$ref](https://github.com/epoberezkin/ajv#combining-schemas-with-ref), [\$merge and \$patch](https://github.com/epoberezkin/ajv#merge-and-patch-keywords) - to reduce the code repetition.

## Instal
## Install

```bash
$ npm i egg-ajv --save
```

## Configuration
## Usage & configuration

Change `${app_root}/config/plugin.js` to enable egg-ajv plugin:
- Enable plugin in `config/plugin.js`

```js
exports.ajv = {
enable: true,
package: 'egg-ajv',
package: "egg-ajv"
};
```

Configure ajv information in `${app_root}/config/config.default.js`:
- Edit your own configurations in `config/config.{env}.js`

```javascript
config.ajv = {
keyword: 'schema', // to indicate the namespace and path of schemas, default as 'schema'
allErrors: true, // required for custom error message
jsonPointers: true, // required for custom error message
}
/**
* default: "schema"
* to indicate the namespace and path of schemas, default as 'schema'
*/
keyword: "schema",
/**
* default: true
* see https://github.com/epoberezkin/ajv#filtering-data
*/
removeAdditional: true,
/**
* default: true
* see https://github.com/epoberezkin/ajv#assigning-defaults
*/
useDefaults: true,
/**
* default: true
* see https://github.com/epoberezkin/ajv#coercing-data-types
*/
coerceTypes: true,
/**
* default: false
* see https://github.com/epoberezkin/ajv-errors#options
*/
keepErrors: false,
/**
* default: false
* see https://github.com/epoberezkin/ajv-errors#options
*/
singleError: false
};
```

And all [the options of Ajv](https://github.com/epoberezkin/ajv#options) is supported.

## API

```javascript
/**
* json validation
*
* @param {string|object} schema - string for schema id and object for Ajv rules
* @param {object} value - default as ctx.request.body
* @return {undefine} throw an exception instead
*/
async validate(schema, value) {}
/**
* json validation
*
* @param {String| Object} schema string for schema id and object for Ajv rules
* @param {Object} value default as ctx.request.body
* @return {Object} type converted value
*/
async validate(schema, value) {}
```

## Usage
Expand All @@ -55,92 +82,90 @@ We need to put our schemas at the `/app/${config.ajv.keyword}` directory, which
#### A Simple Example:

```javascript

// app/schema/definition.js

module.exports = {
int: { type: 'integer' },
str: { type: 'string' },
int: { type: "integer" },
str: { type: "string" }
};

// app/schema/user.js

module.exports = {
properties: {
id: {
$ref: 'schema.definition#/int',
$ref: "schema.definition#/int"
},
name: {
type: 'string',
type: "string"
},
password: {
type: 'string',
},
type: "string"
}
},
required: [ 'name', 'password', 'id' ],
required: ["name", "password", "id"],
$async: true,
additionalProperties: false,
additionalProperties: false
};

// app/controller/user.js

exports.create = async ctx => {
await this.ctx.validate('schema.pagination', this.ctx.request.body);
await this.ctx.validate("schema.pagination", this.ctx.request.body);
};

```

#### Another Example with the use of $merge
#### Another Example with the use of \$merge

```javascript

// app/schema/user.js

module.exports = {
properties: {
name: {
type: 'string',
type: "string"
},
password: {
type: 'string',
},
type: "string"
}
},
required: [ 'name', 'password' ],
required: ["name", "password"],
$async: true,
additionalProperties: false,
additionalProperties: false
};

// app/controller/user.js

const rule = {
const rule = {
$async: true,
$merge: {
source: {
properties: {
user: { $ref: 'schema.user#' },
},
user: { $ref: "schema.user#" }
}
},
with: {
properties: {
age: {
type: 'number',
},
type: "number"
}
},
// array would be overrided instead of merged
required: [ 'password', 'name', 'age' ],
},
},
required: ["password", "name", "age"]
}
}
};

exports.create = async ctx => {
await this.ctx.validate(rule, this.ctx.request.body);
// or
await this.ctx.validate(this.app.schema.user);
};

```

### Custom Error Message

```
```JSON
{
"type": "object",
"properties": {
Expand All @@ -163,31 +188,45 @@ check detail at [ajv-errors](https://github.com/epoberezkin/ajv-errors)

An exception will be thrown when a validation failed, so we'd better catch it at the middleware, but Egg-ajv doesn't do it for the sake of expansibility.

Try something like this:
Try something like this:

```javascript

// app/middleware/error.js
const { ValidationError } = require('ajv');
const { ValidationError } = require("egg-ajv/error");

module.exports = () =>
function*(next) {
try {
yield next;
} catch (e) {
if (e instanceof ValidationError) {
this.body = {
code: 422,
msg: "请求参数错误",
errors: e.errors
};
this.status = 422;
} else {
throw e;
}
}
};
```

Or catch the error by `onerror` in `config/config.{env}.js` files

```js
// config/config.default.js
const { ValidationError } = require("egg-ajv/error");

module.exports = () => function* (next) {
try {
yield next;
} catch (e) {
exports.onerror = {
all: (e, ctx) => {
if (e instanceof ValidationError) {
this.body = {
code: 422,
msg: '请求参数错误',
errors: e.errors,
};
this.status = 422;
} else {
throw e;
ctx.body = JSON.stringify(e.errors);
ctx.status = 422;
}
}
};

```

For more information, check the [test app example](./test/fixtures/apps/ajv-test)

12 changes: 6 additions & 6 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
'use strict';

const assert = require('assert');
const {
join,
} = require('path');
const { join } = require('path');

module.exports = app => {
const { keyword = 'schema' } = app.config.ajv || {};
const { keyword } = app.config.ajv;
app.loader.loadToApp(join(app.baseDir, `app/${keyword}`), keyword, {
initializer(exp, { path, pathName }) {
assert(app.ajv.validateSchema(exp), `${path} should be a valid schema`);
app.ajv.addSchema(Object.assign({ $id: pathName }, exp), pathName);
app.ajv.addSchema(
Object.assign({ $id: pathName, $async: true }, exp),
pathName
);
return exp;
},
});

};
4 changes: 2 additions & 2 deletions app/extend/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ module.exports = {
}, config));
require('ajv-merge-patch')(ajv);
require('ajv-errors')(ajv, {
keepErrors: config.keepErrors || false,
singleError: config.singleError || false,
keepErrors: config.keepErrors,
singleError: config.singleError,
});
this[APPAJV] = ajv;
}
Expand Down
13 changes: 8 additions & 5 deletions app/extend/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@
const assert = require('assert');

module.exports = {

/**
* json validation
*
* @param {string|object} schema string for schema id and object for Ajv rules
* @param {any} value default as ctx.request.body
* @return {undefine} throw an exception instead
* @param {String| Object} schema string for schema id and object for Ajv rules
* @param {Object} value default as ctx.request.body
* @return {Object} type converted value
*/
async validate(schema, value) {
value = value || this.request.body;
let validater = null;
const { ajv } = this.app;
if (typeof schema === 'string') {
validater = ajv.getSchema(schema);
assert(validater, `Schema - ${schema} - IS NOT FOUND`);
} else {
schema = Object.assign(schema, { $async: true });
assert(ajv.validateSchema(schema), `Schema is not valid: ${schema}`);
validater = ajv.compile(schema);
}

return validater(value || this.ctx.request.body || {});
await validater(value);

return value;
},
};
6 changes: 6 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
* @property {String} SOME_KEY - some description
*/
exports.ajv = {
keyword: 'schema',
removeAdditional: true, // https://github.com/epoberezkin/ajv#filtering-data
useDefaults: true, // https://github.com/epoberezkin/ajv#assigning-defaults
coerceTypes: true, // https://github.com/epoberezkin/ajv#coercing-data-types
keepErrors: false,
singleError: false,
};
Loading