-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
ADD z-schema validator plugin #1157
Changes from all commits
76fdb74
6c69994
8eaeb54
0b1f6c8
3479066
cb3b7ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
declare let _default: {}; | ||
export default _default; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "rxdb-plugin-validate-z-schema", | ||
"main": "../../dist/lib/plugins/validate-z-schema.js", | ||
"jsnext:main": "../../dist/es/plugins/validate-z-schema.js", | ||
"module": "../../dist/es/plugins/validate-z-schema.js" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* this plugin validates documents before they can be inserted into the RxCollection. | ||
* It's using z-schema as jsonschema-validator | ||
* @link https://github.com/zaggino/z-schema | ||
*/ | ||
import ZSchema from 'z-schema'; | ||
import { | ||
newRxError | ||
} from '../rx-error'; | ||
import { | ||
requestIdleCallbackIfAvailable | ||
} from '../util'; | ||
|
||
/** | ||
* cache the validators by the schema-hash | ||
* so we can reuse them when multiple collections have the same schema | ||
* @type {Object<string, any>} | ||
*/ | ||
const validatorsCache = {}; | ||
|
||
|
||
/** | ||
* returns the parsed validator from z-schema | ||
* @param {string} [schemaPath=''] if given, the schema for the sub-path is used | ||
* @ | ||
*/ | ||
function _getValidator(rxSchema, schemaPath = '') { | ||
const hash = rxSchema.hash; | ||
if (!validatorsCache[hash]) validatorsCache[hash] = {}; | ||
const validatorsOfHash = validatorsCache[hash]; | ||
|
||
if (!validatorsOfHash[schemaPath]) { | ||
const schemaPart = schemaPath === '' ? rxSchema.jsonID : rxSchema.getSchemaByObjectPath(schemaPath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand where does the |
||
|
||
if (!schemaPart) { | ||
throw newRxError('VD1', { | ||
schemaPath: schemaPath | ||
}); | ||
} | ||
|
||
const validator = new ZSchema(); | ||
validatorsOfHash[schemaPath] = (obj) => { | ||
validator.validate(obj, schemaPart); | ||
return validator; | ||
}; | ||
} | ||
|
||
return validatorsOfHash[schemaPath]; | ||
} | ||
|
||
/** | ||
* validates the given object against the schema | ||
* @param {any} obj | ||
* @param {String} [schemaPath=''] if given, the sub-schema will be validated | ||
* @throws {RxError} if not valid | ||
* @return {any} obj if validation successful | ||
*/ | ||
const validate = function (obj, schemaPath = '') { | ||
const validator = _getValidator(this, schemaPath); | ||
const useValidator = validator(obj); | ||
/** @type {ZSchema.SchemaErrorDetail[]} */ | ||
const errors = useValidator.getLastErrors(); | ||
if (!errors) return obj; | ||
else { | ||
const formattedZSchemaErrors = errors.map(({ title, description, message }) => ({ | ||
title, | ||
description, | ||
message | ||
})); | ||
throw newRxError('VD2', { | ||
errors: formattedZSchemaErrors, | ||
schemaPath, | ||
obj, | ||
schema: this.jsonID | ||
}); | ||
} | ||
}; | ||
|
||
const runAfterSchemaCreated = rxSchema => { | ||
// pre-generate the validator-z-schema from the schema | ||
requestIdleCallbackIfAvailable(() => _getValidator.bind(rxSchema, rxSchema)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why but I had to .bind here... otherwise the test would work, but in the browser it wouldn't.. if you have a better way to handle this let me know |
||
}; | ||
|
||
export const rxdb = true; | ||
export const prototypes = { | ||
/** | ||
* set validate-function for the RxSchema.prototype | ||
* @param {[type]} prototype of RxSchema | ||
*/ | ||
RxSchema: (proto) => { | ||
proto._getValidator = _getValidator; | ||
proto.validate = validate; | ||
} | ||
}; | ||
export const hooks = { | ||
createRxSchema: runAfterSchemaCreated | ||
}; | ||
|
||
export default { | ||
rxdb, | ||
prototypes, | ||
hooks | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import assert from 'assert'; | ||
import AsyncTestUtil from 'async-test-util'; | ||
import config from './config'; | ||
|
||
import * as schemaObjects from '../helper/schema-objects'; | ||
import * as schemas from '../helper/schemas'; | ||
|
||
import * as util from '../../dist/lib/util'; | ||
import Core from '../../dist/lib/core'; | ||
Core.plugin(require('../../plugins/validate-z-schema')); | ||
Core.plugin(require('../../plugins/key-compression')); | ||
Core.plugin(require('../../plugins/error-messages')); | ||
Core.plugin(require('pouchdb-adapter-memory')); | ||
|
||
config.parallel('validate-z-schema.node.js', () => { | ||
describe('validation', () => { | ||
describe('positive', () => { | ||
it('should not throw', async () => { | ||
const db = await Core.create({ | ||
name: util.randomCouchString(10), | ||
adapter: 'memory' | ||
}); | ||
const col = await db.collection({ | ||
name: 'humans', | ||
schema: schemas.human | ||
}); | ||
|
||
const doc = await col.insert(schemaObjects.human()); | ||
assert.ok(doc); | ||
|
||
db.destroy(); | ||
}); | ||
}); | ||
describe('negative', () => { | ||
it('should not validate wrong data', async () => { | ||
const db = await Core.create({ | ||
name: util.randomCouchString(10), | ||
adapter: 'memory' | ||
}); | ||
const col = await db.collection({ | ||
name: 'humans', | ||
schema: schemas.human | ||
}); | ||
|
||
await AsyncTestUtil.assertThrows( | ||
() => col.insert({ | ||
foo: 'bar' | ||
}), | ||
'RxError' | ||
); | ||
|
||
db.destroy(); | ||
}); | ||
it('should have the correct params in error', async () => { | ||
const db = await Core.create({ | ||
name: util.randomCouchString(10), | ||
adapter: 'memory' | ||
}); | ||
const col = await db.collection({ | ||
name: 'humans', | ||
schema: schemas.human | ||
}); | ||
|
||
let error = null; | ||
try { | ||
await col.insert({ | ||
foo: 'bar' | ||
}); | ||
} catch (e) { | ||
error = e; | ||
} | ||
|
||
console.log(error); | ||
|
||
assert.ok(error); | ||
assert.ok(error.parameters.errors.length > 0); | ||
db.destroy(); | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that 'ajv' is not listed here, are users supposed to install themselves? if so I guess I should remove z-schema from here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think ajv is missing. Z-schema schould stay there. Im offline since monday btw, i can help then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what version of ajv should I add?