Skip to content

Commit

Permalink
[FAB-14537] components ref components
Browse files Browse the repository at this point in the history
Change-Id: I8123fbfe00e97630b3a6256f1b0b1349c8d9199b
Signed-off-by: awjh-ibm <[email protected]>
  • Loading branch information
awjh-ibm committed Mar 13, 2019
1 parent c655d22 commit c406864
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 140 deletions.
2 changes: 1 addition & 1 deletion fabric-contract-api/lib/annotations/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ module.exports.Property = function Property (name, type) {

properties.push({
name,
schema: utils.generateSchema(type)
schema: utils.generateSchema(type, false)
});

Reflect.defineMetadata('fabric:object-properties', properties, target);
Expand Down
10 changes: 5 additions & 5 deletions fabric-contract-api/lib/annotations/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,29 @@ module.exports.findByValue = function findByValue(arr, primaryField, id) {
return null;
};

const generateSchema = (type) => {
const generateSchema = (type, fullPath = true) => {
if (isPrimitive(type)) {
return {
type: type.toLowerCase()
};
} else if (isArray(type)) {
const subType = getSubArray(type);
const subType = getSubArray(type, fullPath);

return {
type: 'array',
items: generateSchema(subType)
items: generateSchema(subType, fullPath)
};
} else if (isMap(type)) {
const subType = getSubMap(type);

return {
type: 'object',
additionalProperties: generateSchema(subType)
additionalProperties: generateSchema(subType, fullPath)
};
}

return {
$ref: refPath + type
$ref: (fullPath ? refPath : '') + type
};
};
module.exports.generateSchema = generateSchema;
Expand Down
13 changes: 13 additions & 0 deletions fabric-contract-api/test/unit/annotations/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ describe('utils', () => {
});
});

it ('should return a ref path for a non array and non primitive type and not use full path', () => {
expect(utils.generateSchema('Duck', false)).to.deep.equal({
$ref: 'Duck'
});
});

it ('should recurse for array types', () => {
expect(utils.generateSchema('Duck[]')).to.deep.equal({
type: 'array',
Expand All @@ -129,6 +135,13 @@ describe('utils', () => {
}
});

expect(utils.generateSchema('Array<Duck>', false)).to.deep.equal({
type: 'array',
items: {
$ref: 'Duck'
}
});

expect(utils.generateSchema('Array<string>')).to.deep.equal({
type: 'array',
items: {
Expand Down
29 changes: 1 addition & 28 deletions fabric-shim/lib/contract-spi/chaincodefromcontract.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,33 +83,6 @@ class ChaincodeFromContract {
* for arguments.
*/
_compileSchemas() {

const schemaList = [];
for (const name in this.metadata.components.schemas) {
const s = this.metadata.components.schemas[name];
const props = {};
s.properties.forEach((e) => {
props[e.name] = e;
});

s.properties = props;
schemaList.push(s);
}

if (schemaList.length > 0) {
// provide the list of schemas (of the complex types) to AJV, identified by name

const ajv = this._ajv(schemaList);
// create validators for each complex type
// have lowercases the ids
this.contractImplementations.schemas = {};
schemaList.forEach((e) => {
const id = e.$id;
this.contractImplementations.schemas[id] = {};
this.contractImplementations.schemas[id].validator = ajv.getSchema(id);
});

}
// final step is to setup up data marhsall instances
const requestedSerializer = this.serializers.transaction;
for (const contractName in this.contractImplementations) {
Expand All @@ -121,7 +94,7 @@ class ChaincodeFromContract {
}
/* istanbul ignore next */
_dataMarshall(requestedSerializer) {
return new DataMarshall(requestedSerializer, this.serializers.serializers, this.contractImplementations.schemas);
return new DataMarshall(requestedSerializer, this.serializers.serializers, this.metadata.components.schemas);
}
/* istanbul ignore next */
_ajv(schemaList) {
Expand Down
35 changes: 19 additions & 16 deletions fabric-shim/lib/contract-spi/datamarshall.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ module.exports = class DataMarshall {
* @param {String} requestedSerializer name of the requested serializer
* @param {Object} serializers mapping of names to the implementation of the serializers
*/
constructor(requestedSerializer, serializers, schemas) {
logger.debug('New DataMarshaller', requestedSerializer, serializers, schemas);
constructor(requestedSerializer, serializers, components) {
logger.debug('New DataMarshaller', requestedSerializer, serializers, components);
let cnstr = serializers[requestedSerializer];
if (typeof cnstr === 'string') {
cnstr = require(cnstr);
Expand All @@ -41,7 +41,7 @@ module.exports = class DataMarshall {
});

// the complex type schemas from the metadata
this.schemas = schemas;
this.components = components;
}

/**
Expand Down Expand Up @@ -105,25 +105,28 @@ module.exports = class DataMarshall {
logger.debug(`${loggerPrefix} Expected parameter ${expected}`);
logger.debug(`${loggerPrefix} Supplied parameter ${supplied}`);
// check the type
const schema = expected.schema;

let validator;

if (schema.type) {
validator = this.ajv.compile(schema);
} else if (schema.$ref) {
const n = schema.$ref.lastIndexOf('/');
const typeName = schema.$ref.substring(n + 1);
validator = this.schemas[typeName].validator;
} else {
throw new Error(`Incorrect type information ${JSON.stringify(schema)}`);
const schema = {
properties: {
prop: expected.schema
},
components: {
schemas: this.components
}
};

if (!expected.schema.type && !expected.schema.$ref) {
throw new Error(`Incorrect type information ${JSON.stringify(expected.schema)}`);
}

const validator = this.ajv.compile(schema);

const {value, validateData} = this.fromWireBuffer(supplied, expected.schema, loggerPrefix);
const valid = validator(validateData);

if (!valid) {
const errors = JSON.stringify(validator.errors);
const errors = JSON.stringify(validator.errors.map((err) => {
return err.message;
}));
logger.debug(`${loggerPrefix} ${errors}`);
throw new Error(`Unable to validate parameter due to ${errors}`);
}
Expand Down
54 changes: 36 additions & 18 deletions fabric-shim/test/unit/contract-spi/datamarshall.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('datamarshall.js', () => {
let dm;

beforeEach(() => {
dm = new DataMarshall('jsonSerializer', defaultSerialization.serializers);
dm = new DataMarshall('jsonSerializer', defaultSerialization.serializers, {});

dm.fromWireBuffer = sinon.stub()
.onFirstCall().returns({value: 'some value', validateData: 'some validate data'})
Expand Down Expand Up @@ -198,14 +198,14 @@ describe('datamarshall.js', () => {
]};

const validateStub = sinon.stub().returns(false);
validateStub.errors = ['list', 'of', 'reasons', 'why', 'params', 'were', 'wrong'];
validateStub.errors = [{message: 'list'}, {message: 'of'}, {message: 'reasons'}, {message: 'why'}, {message: 'params'}, {message: 'were'}, {message: 'wrong'}];
dm.ajv.compile = sinon.stub().returns(validateStub);

expect(() => {
dm.handleParameters(fn, ['"one"'], 'logging prefix');
}).to.throw(`Unable to validate parameter due to ${JSON.stringify(validateStub.errors)}`);
}).to.throw(`Unable to validate parameter due to ${JSON.stringify(validateStub.errors.map((err) => { return err.message; }))}`); // eslint-disable-line
sinon.assert.calledWith(dm.fromWireBuffer, '"one"', {type: 'string'}, 'logging prefix');
sinon.assert.calledWith(dm.ajv.compile, {type:'string'});
sinon.assert.calledWith(dm.ajv.compile, {components: {schemas: {}}, properties: {prop: {type: 'string'}}});
sinon.assert.calledWith(validateStub, 'some validate data');
});

Expand All @@ -214,22 +214,31 @@ describe('datamarshall.js', () => {
{
name:'one',
schema:{
$ref:'#components/someComponent'
$ref:'#/components/schemas/someComponent'
}
}
]};

const validateStub = sinon.stub().returns(false);
validateStub.errors = ['list', 'of', 'reasons', 'why', 'params', 'were', 'wrong'];
validateStub.errors = [{message: 'list'}, {message: 'of'}, {message: 'reasons'}, {message: 'why'}, {message: 'params'}, {message: 'were'}, {message: 'wrong'}];
dm.ajv.compile = sinon.stub().returns(validateStub);

dm.schemas = {};
dm.schemas.someComponent = {};
dm.schemas.someComponent.validator = validateStub;
dm.components = {
someComponent: {
$id: 'someComponent',
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
};

expect(() => {
dm.handleParameters(fn, ['"one"'], 'logging prefix');
}).to.throw(`Unable to validate parameter due to ${JSON.stringify(validateStub.errors)}`);
sinon.assert.calledWith(dm.fromWireBuffer, '"one"', {$ref:'#components/someComponent'}, 'logging prefix');
}).to.throw(`Unable to validate parameter due to ${JSON.stringify(validateStub.errors.map((err) => { return err.message; }))}`); // eslint-disable-line
sinon.assert.calledWith(dm.fromWireBuffer, '"one"', {$ref:'#/components/schemas/someComponent'}, 'logging prefix');
sinon.assert.calledWith(validateStub, 'some validate data');
});

Expand All @@ -244,7 +253,7 @@ describe('datamarshall.js', () => {
{
name:'two',
schema:{
$ref: '#components/someComponent'
$ref: '#/components/schemas/someComponent'
}
}
]};
Expand All @@ -253,18 +262,27 @@ describe('datamarshall.js', () => {

dm.ajv.compile = sinon.stub().returns(validateStub);

dm.schemas = {};
dm.schemas.someComponent = {};
dm.schemas.someComponent.validator = validateStub;
dm.components = {
someComponent: {
$id: 'someComponent',
type: 'object',
properties: {
name: {
type: 'string'
}
}
}
};

const returned = dm.handleParameters(fn, ['"one"', '"two"'], 'logging prefix');

sinon.assert.calledTwice(dm.fromWireBuffer);
sinon.assert.calledWith(dm.fromWireBuffer, '"one"', {type: 'string'}, 'logging prefix');
sinon.assert.calledWith(dm.fromWireBuffer, '"two"', {$ref: '#components/someComponent'}, 'logging prefix');
sinon.assert.calledWith(dm.fromWireBuffer, '"two"', {$ref: '#/components/schemas/someComponent'}, 'logging prefix');

sinon.assert.calledOnce(dm.ajv.compile);
sinon.assert.calledWith(dm.ajv.compile, {type:'string'});
sinon.assert.calledTwice(dm.ajv.compile);
sinon.assert.calledWith(dm.ajv.compile, {components: {schemas: dm.components}, properties: {prop: {type: 'string'}}});
sinon.assert.calledWith(dm.ajv.compile, {components: {schemas: dm.components}, properties: {prop: {$ref: '#/components/schemas/someComponent'}}});

sinon.assert.calledTwice(validateStub);
sinon.assert.calledWith(validateStub, 'some validate data');
Expand Down
13 changes: 10 additions & 3 deletions test/fv/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;
const utils = require('./utils');
const {SHORT_INC, LONG_STEP} = utils.TIMEOUTS;
const {SHORT_INC, SHORT_STEP, LONG_STEP} = utils.TIMEOUTS;

describe('Typescript chaincode', () => {
const suite = 'annotations';
Expand All @@ -18,10 +18,17 @@ describe('Typescript chaincode', () => {
describe('Scenario', () => {
it('should write an asset', async function () {
this.timeout(SHORT_INC);
await utils.invoke(suite, 'TestContract:createAsset', ['GLD', 'GOLD_BAR', '100']);
await utils.invoke(suite, 'TestContract:createAsset', ['GLD', 'GOLD_BAR', '100', 'EXTRA_ID', '50']);
const payload = JSON.parse(await utils.query(suite, 'TestContract:getAsset', ['GLD']));

expect(payload).to.eql({id: 'GLD', name: 'GOLD_BAR', value: 100});
expect(payload).to.eql({id: 'GLD', name: 'GOLD_BAR', value: 100, extra: {id: 'EXTRA_ID', value: 50}});
});

it ('should update an asset', async function() {
this.timeout(SHORT_STEP);
await utils.invoke(suite, 'TestContract:updateAsset', [JSON.stringify({id: 'GLD', name: 'GOLD_BAR', value: 200, extra: {id: 'EXTRA_ID', value: 100}})]);
const payload = JSON.parse(await utils.query(suite, 'TestContract:getAsset', ['GLD']));
expect(payload).to.eql({id: 'GLD', name: 'GOLD_BAR', value: 200, extra: {id: 'EXTRA_ID', value: 100}});
});

it('should handle the getMetadata', async function () {
Expand Down
Loading

0 comments on commit c406864

Please sign in to comment.