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

@@ -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;