Fix and reorganize schemas and switch to ajv validator

This commit is contained in:
Chris Clark
2015-10-30 13:57:01 -07:00
parent 2aa1695b74
commit c7b021c7be
76 changed files with 168 additions and 188 deletions

48
npm-shrinkwrap.json generated
View File

@@ -4,6 +4,22 @@
"npm-shrinkwrap-version": "5.4.0",
"node-version": "v0.12.7",
"dependencies": {
"ajv": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-1.4.8.tgz",
"dependencies": {
"json-stable-stringify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.0.tgz",
"dependencies": {
"jsonify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
}
}
}
}
},
"babel-runtime": {
"version": "5.8.29",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
@@ -48,34 +64,6 @@
}
}
},
"is-my-json-valid": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.2.tgz",
"dependencies": {
"generate-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
},
"generate-object-property": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
"dependencies": {
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
}
}
},
"jsonpointer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz"
},
"xtend": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
}
}
},
"lodash": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
@@ -183,8 +171,8 @@
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
},
"elliptic": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.0.tgz",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.1.tgz",
"dependencies": {
"inherits": {
"version": "2.0.1",

View File

@@ -15,10 +15,10 @@
"test": "test"
},
"dependencies": {
"ajv": "^1.4.8",
"babel-runtime": "^5.5.4",
"bignumber.js": "^2.0.3",
"https-proxy-agent": "^1.0.0",
"is-my-json-valid": "^2.12.2",
"lodash": "^3.1.0",
"ripple-address-codec": "^2.0.1",
"ripple-binary-codec": "^0.0.6",

View File

@@ -3,114 +3,101 @@
'use strict';
const _ = require('lodash');
const assert = require('assert');
const validator = require('is-my-json-valid');
const Ajv = require('ajv');
const ValidationError = require('./errors').ValidationError;
const {isValidAddress} = require('ripple-address-codec');
let SCHEMAS = {};
function loadSchemas() {
// listed explicitly for webpack (instead of scanning schemas directory)
const schemas = [
require('./schemas/address.json'),
require('./schemas/adjustment.json'),
require('./schemas/amount.json'),
require('./schemas/amountbase.json'),
require('./schemas/balance.json'),
require('./schemas/blob.json'),
require('./schemas/currency.json'),
require('./schemas/get-account-info.json'),
require('./schemas/get-balances.json'),
require('./schemas/get-balance-sheet'),
require('./schemas/balance-sheet-options.json'),
require('./schemas/get-ledger.json'),
require('./schemas/get-orderbook.json'),
require('./schemas/get-orders.json'),
require('./schemas/get-paths.json'),
require('./schemas/get-server-info.json'),
require('./schemas/get-settings.json'),
require('./schemas/get-transaction.json'),
require('./schemas/get-transactions.json'),
require('./schemas/get-trustlines.json'),
require('./schemas/hash128.json'),
require('./schemas/hash256.json'),
require('./schemas/instructions.json'),
require('./schemas/issue.json'),
require('./schemas/ledger-options.json'),
require('./schemas/ledgerversion.json'),
require('./schemas/max-adjustment.json'),
require('./schemas/memo.json'),
require('./schemas/order-cancellation-transaction.json'),
require('./schemas/order-cancellation.json'),
require('./schemas/order-change.json'),
require('./schemas/order-transaction.json'),
require('./schemas/order.json'),
require('./schemas/orderbook-orders.json'),
require('./schemas/orderbook.json'),
require('./schemas/orders-options.json'),
require('./schemas/outcome.json'),
require('./schemas/pathfind.json'),
require('./schemas/payment-transaction.json'),
require('./schemas/payment.json'),
require('./schemas/quality.json'),
require('./schemas/api-options.json'),
require('./schemas/sequence.json'),
require('./schemas/settings-options.json'),
require('./schemas/settings-transaction.json'),
require('./schemas/settings.json'),
require('./schemas/sign.json'),
require('./schemas/signed-value.json'),
require('./schemas/submit.json'),
require('./schemas/suspended-payment-cancellation.json'),
require('./schemas/suspended-payment-execution.json'),
require('./schemas/suspended-payment-creation.json'),
require('./schemas/timestamp.json'),
require('./schemas/transaction-options.json'),
require('./schemas/transactions-options.json'),
require('./schemas/trustline-transaction.json'),
require('./schemas/trustline.json'),
require('./schemas/trustlines-options.json'),
require('./schemas/tx.json'),
require('./schemas/uint32.json'),
require('./schemas/value.json'),
require('./schemas/prepare.json'),
require('./schemas/ledger-closed.json')
require('./schemas/objects/address.json'),
require('./schemas/objects/adjustment.json'),
require('./schemas/objects/amount.json'),
require('./schemas/objects/amount-base.json'),
require('./schemas/objects/balance.json'),
require('./schemas/objects/blob.json'),
require('./schemas/objects/currency.json'),
require('./schemas/output/get-account-info.json'),
require('./schemas/output/get-balances.json'),
require('./schemas/output/get-balance-sheet'),
require('./schemas/input/balance-sheet-options.json'),
require('./schemas/output/get-ledger.json'),
require('./schemas/output/get-orderbook.json'),
require('./schemas/output/get-orders.json'),
require('./schemas/output/get-paths.json'),
require('./schemas/output/get-server-info.json'),
require('./schemas/output/get-settings.json'),
require('./schemas/output/get-transaction.json'),
require('./schemas/output/get-transactions.json'),
require('./schemas/output/get-trustlines.json'),
require('./schemas/objects/hash128.json'),
require('./schemas/objects/hash256.json'),
require('./schemas/input/instructions.json'),
require('./schemas/objects/issue.json'),
require('./schemas/input/ledger-options.json'),
require('./schemas/objects/ledgerversion.json'),
require('./schemas/objects/max-adjustment.json'),
require('./schemas/objects/memo.json'),
require('./schemas/output/order-cancellation-transaction.json'),
require('./schemas/input/order-cancellation.json'),
require('./schemas/output/order-change.json'),
require('./schemas/output/order-transaction.json'),
require('./schemas/input/order.json'),
require('./schemas/output/orderbook-orders.json'),
require('./schemas/input/orderbook.json'),
require('./schemas/input/orders-options.json'),
require('./schemas/output/outcome.json'),
require('./schemas/input/pathfind.json'),
require('./schemas/output/payment-transaction.json'),
require('./schemas/input/payment.json'),
require('./schemas/objects/quality.json'),
require('./schemas/input/api-options.json'),
require('./schemas/objects/sequence.json'),
require('./schemas/input/settings-options.json'),
require('./schemas/output/settings-transaction.json'),
require('./schemas/input/settings.json'),
require('./schemas/output/sign.json'),
require('./schemas/objects/signed-value.json'),
require('./schemas/output/submit.json'),
require('./schemas/input/suspended-payment-cancellation.json'),
require('./schemas/input/suspended-payment-execution.json'),
require('./schemas/input/suspended-payment-creation.json'),
require('./schemas/input/transaction-options.json'),
require('./schemas/input/transactions-options.json'),
require('./schemas/output/trustline-transaction.json'),
require('./schemas/input/trustline.json'),
require('./schemas/input/trustlines-options.json'),
require('./schemas/objects/tx-json.json'),
require('./schemas/objects/uint32.json'),
require('./schemas/objects/value.json'),
require('./schemas/output/prepare.json'),
require('./schemas/output/ledger-closed.json'),
require('./schemas/objects/source-adjustment.json'),
require('./schemas/objects/destination-adjustment.json'),
require('./schemas/objects/tag.json'),
require('./schemas/objects/lax-amount.json'),
require('./schemas/objects/lax-lax-amount.json'),
require('./schemas/objects/min-adjustment.json'),
require('./schemas/objects/lax-adjustment.json')
];
const titles = _.map(schemas, schema => schema.title);
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
return _.indexBy(schemas, 'title');
const ajv = new Ajv();
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title));
ajv.addFormat('address', isValidAddress);
return ajv;
}
function formatSchemaError(error) {
try {
return error.field + ' ' + error.message
+ (error.value ? ' (' + JSON.stringify(error.value) + ')' : '');
} catch (err) {
return error.field + ' ' + error.message;
}
}
function formatSchemaErrors(errors) {
return errors.map(formatSchemaError).join(', ');
}
const ajv = loadSchemas();
function schemaValidate(schemaName: string, object: any): void {
const formats = {address: isValidAddress};
const options = {schemas: SCHEMAS, formats: formats,
verbose: true, greedy: true};
const schema = SCHEMAS[schemaName];
if (schema === undefined) {
throw new Error('schema not found for: ' + schemaName);
}
const validate = validator(schema, options);
const isValid = validate(object);
const isValid = ajv.validate(schemaName, object);
if (!isValid) {
throw new ValidationError(formatSchemaErrors(validate.errors));
throw new ValidationError(ajv.errorsText());
}
}
SCHEMAS = loadSchemas();
module.exports = {
schemaValidate
};

View File

@@ -3,6 +3,6 @@
"title": "blob",
"description": "An uppercase hex string representation of a transaction",
"type": "string",
"minLength": "1",
"minLength": 1,
"pattern": "^[0-9A-F]*$"
}

View File

@@ -3,7 +3,7 @@
"title": "destinationAdjustment",
"type": "object",
"oneOf": [
{"$ref": "adjustment"},
{"$ref": "laxAdjustment"},
{"$ref": "minAdjustment"}
]
}

View File

@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "laxAdjustment",
"type": "object",
"properties": {
"address": {"$ref": "address"},
"amount": {"$ref": "laxAmount"},
"tag": {"$ref": "tag"}
},
"required": ["address", "amount"],
"additionalProperties": false
}

View File

@@ -3,7 +3,7 @@
"title": "sourceAdjustment",
"type": "object",
"oneOf": [
{"$ref": "adjustment"},
{"$ref": "laxAdjustment"},
{"$ref": "maxAdjustment"}
]
}

View File

@@ -6,7 +6,7 @@
"properties": {
"feeBase": {"type": "integer", "minimum": 0},
"feeReference": {"type": "integer", "minimum": 0},
"ledgerHash": {"$ref": "ledgerHash"},
"ledgerHash": {"$ref": "hash256"},
"ledgerVersion": {"$ref": "ledgerVersion"},
"ledgerTimestamp": {"type": "string", "format": "date-time"},
"reserveBase": {"type": "integer", "minimum": 0},

View File

@@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "timestamp",
"description": "An ISO 8601 combined date and time timestamp",
"type": "string",
"pattern": "^$|^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T(2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](Z|[+](2[0-3]|[01][0-9]):[0-5][0-9])$"
}

View File

@@ -822,7 +822,7 @@ describe('RippleAPI', function() {
it('schema not found error', function() {
assert.throws(function() {
schemaValidator.schemaValidate('unexisting', 'anything');
}, /schema not found/);
}, /no schema/);
});
});

View File

@@ -2,11 +2,11 @@
const _ = require('lodash');
const addresses = require('../addresses');
module.exports = function(request, options={}) {
module.exports = function(request, options = {}) {
_.defaults(options, {
account: addresses.ACCOUNT,
validated: true
});
});
return JSON.stringify({
'id': request.id,
@@ -23,13 +23,13 @@ module.exports = function(request, options={}) {
'currency': 'EUR',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '17.70155237781915'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '1122.990930900328'
}
},
}
},
{
'flags': 0,
'seq': 757002,
@@ -37,13 +37,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '18.46856867857617'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rpDMez6pm6dBve2TJsmDpv7Yae6V5Pyvy2',
'value': '19.50899530491766'
}
},
}
},
{
'flags': 0,
'seq': 756999,
@@ -51,13 +51,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '19.11697137482289'
},
},
'taker_pays': {
'currency': 'EUR',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '750'
}
},
}
},
{
'flags': 0,
'seq': 757003,
@@ -65,13 +65,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '14.40727807030772'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rpDMez6pm6dBve2TJsmDpv7Yae6V5Pyvy2',
'value': '1445.796633544794'
}
},
}
},
{
'flags': 0,
'seq': 782148,
@@ -79,13 +79,13 @@ module.exports = function(request, options={}) {
'currency': 'NZD',
'issuer': 'rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc',
'value': '9.178557969538755'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '750'
}
},
}
},
{
'flags': 0,
'seq': 787368,
@@ -93,13 +93,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '9.94768291869523'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '500'
}
},
}
},
{
'flags': 0,
'seq': 787408,
@@ -107,13 +107,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '9.994805759894176'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '10000'
}
},
}
},
{
'flags': 0,
'seq': 803438,
@@ -121,13 +121,13 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '11.67691646304319'
},
},
'taker_pays': {
'currency': 'MXN',
'issuer': 'rG6FZ31hDHN1K5Dkbma3PSB5uVCuVVRzfn',
'value': '15834.53653918684'
}
},
}
},
{
'flags': 0,
'seq': 807858,
@@ -135,13 +135,13 @@ module.exports = function(request, options={}) {
'currency': 'XAU',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '0.03206299605333101'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '3968.240250979598'
}
},
}
},
{
'flags': 0,
'seq': 807896,
@@ -149,13 +149,13 @@ module.exports = function(request, options={}) {
'currency': 'XAU',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '0.03347459066593226'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '4139.022125516302'
}
},
}
},
{
'flags': 0,
'seq': 814018,
@@ -163,9 +163,9 @@ module.exports = function(request, options={}) {
'currency': 'NZD',
'issuer': 'rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc',
'value': '6.840555705'
},
},
'taker_pays': '115760190000'
},
},
{
'flags': 0,
'seq': 827522,
@@ -173,13 +173,13 @@ module.exports = function(request, options={}) {
'currency': 'EUR',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '14.40843766044656'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '902.4050961259154'
}
},
}
},
{
'flags': 0,
'seq': 833592,
@@ -187,13 +187,13 @@ module.exports = function(request, options={}) {
'currency': 'XAG',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '1.128432823485991'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '1814.887131319799'
}
},
}
},
{
'flags': 0,
'seq': 833591,
@@ -201,13 +201,13 @@ module.exports = function(request, options={}) {
'currency': 'XAG',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '1.128432823485989'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '181.4887131319798'
}
},
}
},
{
'flags': 0,
'seq': 838954,
@@ -215,13 +215,13 @@ module.exports = function(request, options={}) {
'currency': 'XAG',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '0.7283371225235964'
},
},
'taker_pays': {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '118.6872603846736'
}
},
}
},
{
'flags': 0,
'seq': 843730,
@@ -230,8 +230,8 @@ module.exports = function(request, options={}) {
'currency': 'XAU',
'issuer': 'r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH',
'value': '1'
}
},
}
},
{
'flags': 0,
'seq': 844068,
@@ -239,17 +239,17 @@ module.exports = function(request, options={}) {
'currency': 'USD',
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
'value': '17.77537376072202'
},
},
'taker_pays': {
'currency': 'EUR',
'issuer': 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
'value': '750'
}
}
],
}
}
],
'validated': options.validated
},
},
'status': 'success',
'type': 'response'
});
});
};

View File

@@ -6,7 +6,7 @@ const assert = require('assert');
const errors = require('../../src/common/errors');
const validate = require('../../src/common').validate;
const wallet = require('./wallet');
const requests = require('../fixtures/api/requests');
const requests = require('../fixtures/requests');
const RippleAPI = require('../../src').RippleAPI;