Skip to content

Commit

Permalink
fix in mango generator for multiple AND conditions (hyperledger-archi…
Browse files Browse the repository at this point in the history
…ves#3694)

Signed-off-by: andrew-coleman <[email protected]>
  • Loading branch information
andrew-coleman authored and samjsmith committed Mar 27, 2018
1 parent b63229f commit 73dd7d7
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 6 deletions.
16 changes: 14 additions & 2 deletions packages/composer-runtime/lib/querycompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,22 @@ class QueryCompiler {
let rightKey = rightKeys[i];
if( typeof(left[rightKey]) !== 'undefined' ){
// find a matching key between left and right, merge the right key value to the left key
result[rightKey] = Object.assign(left[rightKey], right[rightKey]);
const combined = {};
const leftProperties = Object.getOwnPropertyDescriptors(left[rightKey]);
for(let prop in leftProperties) {
Object.defineProperty(combined, prop, leftProperties[prop]);
}
const rightProperties = Object.getOwnPropertyDescriptors(right[rightKey]);
for(let prop in rightProperties) {
Object.defineProperty(combined, prop, rightProperties[prop]);
}
result[rightKey] = combined;
}else{
// add the right item to the result
result[rightKey] = right[rightKey];
result[rightKey] = Object.create(
Object.getPrototypeOf(right[rightKey]),
Object.getOwnPropertyDescriptors(right[rightKey])
);
}
}
} else { // like left= true, right = false
Expand Down
98 changes: 94 additions & 4 deletions packages/composer-runtime/test/querycompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ describe('QueryCompiler', () => {
o Baa baa
o String[] noises
o Meow[] meows
o DateTime date
}
participant SampleParticipant identified by participantId {
Expand Down Expand Up @@ -194,6 +195,24 @@ describe('QueryCompiler', () => {
SELECT org.acme.sample.SampleAsset
WHERE (meows CONTAINS ((woof == "foo") OR (woof == "noo")))
}
query Q20 {
description: "Select between date range parameters"
statement:
SELECT org.acme.sample.SampleAsset
WHERE (
(date >= _$startTime) AND
(date < _$endTime) AND
(value == _$value))
}
query Q21 {
description: "Select between date range"
statement:
SELECT org.acme.sample.SampleAsset
WHERE (
(date >= "2018-01-01") AND
(date < "2018-02-01") AND
(value == "foo"))
}
`);
queryFile1.validate();
queries = {};
Expand Down Expand Up @@ -221,7 +240,7 @@ describe('QueryCompiler', () => {
const compiledQueryBundle = queryCompiler.compile(queryManager);
compiledQueryBundle.queryCompiler.should.equal(queryCompiler);
compiledQueryBundle.compiledQueries.should.be.an('array');
compiledQueryBundle.compiledQueries.should.have.lengthOf(19);
compiledQueryBundle.compiledQueries.should.have.lengthOf(21);
compiledQueryBundle.compiledQueries.should.all.have.property('name');
compiledQueryBundle.compiledQueries.should.all.have.property('hash');
compiledQueryBundle.compiledQueries.should.all.have.property('generator');
Expand All @@ -234,7 +253,7 @@ describe('QueryCompiler', () => {
it('should visit all of the things', () => {
const compiled = queryCompiler.visit(queryManager, {});
compiled.should.be.an('array');
compiled.should.have.lengthOf(19);
compiled.should.have.lengthOf(21);
compiled.should.all.have.property('name');
compiled.should.all.have.property('hash');
compiled.should.all.have.property('generator');
Expand All @@ -253,7 +272,7 @@ describe('QueryCompiler', () => {
it('should compile all queries in the query manager', () => {
const compiled = queryCompiler.visitQueryManager(queryManager, {});
compiled.should.be.an('array');
compiled.should.have.lengthOf(19);
compiled.should.have.lengthOf(21);
compiled.should.all.have.property('name');
compiled.should.all.have.property('hash');
compiled.should.all.have.property('generator');
Expand All @@ -273,7 +292,7 @@ describe('QueryCompiler', () => {
it('should compile all queries in the query file', () => {
const compiled = queryCompiler.visitQueryFile(queryFile1, {});
compiled.should.be.an('array');
compiled.should.have.lengthOf(19);
compiled.should.have.lengthOf(21);
compiled.should.all.have.property('name');
compiled.should.all.have.property('hash');
compiled.should.all.have.property('generator');
Expand Down Expand Up @@ -317,6 +336,20 @@ describe('QueryCompiler', () => {
compiled.generator({ animalNoise: 'ribbet' }).should.equal('{"selector":{"\\\\$class":"org.acme.sample.SampleAsset","\\\\$registryType":"Asset","\\\\$registryId":"org.acme.sample.SampleAsset","baa.moo.neigh.meow.woof":{"$eq":"ribbet"}}}');
});

it('should compile a query with date range parameters', () => {
const compiled = queryCompiler.visitQuery(queries.Q20, {});
compiled.name.should.equal('Q20');
compiled.generator.should.be.a('function');
compiled.generator({ value: 'foo', startTime: '2018-01-01', endTime: '2018-02-01' }).should.equal('{"selector":{"\\\\$class":"org.acme.sample.SampleAsset","\\\\$registryType":"Asset","\\\\$registryId":"org.acme.sample.SampleAsset","date":{"$gte":"2018-01-01","$lt":"2018-02-01"},"value":{"$eq":"foo"}}}');
});

it('should compile a query with date range', () => {
const compiled = queryCompiler.visitQuery(queries.Q21, {});
compiled.name.should.equal('Q21');
compiled.generator.should.be.a('function');
compiled.generator({}).should.equal('{"selector":{"\\\\$class":"org.acme.sample.SampleAsset","\\\\$registryType":"Asset","\\\\$registryId":"org.acme.sample.SampleAsset","date":{"$gte":"2018-01-01","$lt":"2018-02-01"},"value":{"$eq":"foo"}}}');
});

});

describe('#buildTrivialCompiledQueryGenerator', () => {
Expand Down Expand Up @@ -856,6 +889,63 @@ describe('QueryCompiler', () => {
result.should.deep.equal({someProp:{$lt: 'foo'}, anotherProp: {$eq: 'bar'}});
});

it('should compile an AND-AND expression with same property', () => {
const parameters = {
requiredParameters: [],
parametersToUse: {
start: 5,
end: 10,
foo: 'bar'
}
};
const result = queryCompiler.visitArrayCombinationOperator({
type: 'BinaryExpression',
operator: 'AND',

left: {
type: 'BinaryExpression',
operator: 'AND',
left: {
type: 'BinaryExpression',
operator: '>=',
left: {
type: 'Identifier',
name: 'someProp'
},
right: {
type: 'Identifier',
name: '_$start'
}
},
right: {
type: 'BinaryExpression',
operator: '<',
left: {
type: 'Identifier',
name: 'someProp'
},
right: {
type: 'Identifier',
name: '_$end'
}
}
},
right: {
type: 'BinaryExpression',
operator: '==',
left: {
type: 'Identifier',
name: 'anotherProp'
},
right: {
type: 'Identifier',
name: '_$foo'
}
}
}, parameters);
result.should.deep.equal({someProp:{$gte: 5, $lt: 10}, anotherProp: {$eq: 'bar'}});
});

it('should compile an OR expression', () => {
const result = queryCompiler.visitArrayCombinationOperator({
type: 'BinaryExpression',
Expand Down
90 changes: 90 additions & 0 deletions packages/composer-tests-integration/features/queries.feature
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,45 @@ Feature: Queries steps
}
"""

Scenario: Using the REST API, I can create yet another Commodity
When I make a POST request to /api/Commodity
"""
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
}
"""
Then The response code should be 200
And The response body should be JSON matching
"""
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
}
"""

Scenario: Using the REST API, I can list the Commodities
When I make a GET request to /api/Commodity
Then The response code should be 200
And The response body should be JSON matching
"""
[
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "CC",
Expand Down Expand Up @@ -178,6 +211,14 @@ Feature: Queries steps
And The response body should be JSON matching
"""
[
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "CC",
Expand Down Expand Up @@ -211,6 +252,14 @@ Feature: Queries steps
"quantity": 10,
"owner": "resource:org.acme.biznet.Trader#TRADER1"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "CC",
Expand All @@ -236,6 +285,14 @@ Feature: Queries steps
"quantity": 80,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "EMA",
Expand Down Expand Up @@ -264,6 +321,31 @@ Feature: Queries steps
]
"""

Scenario: Using the REST API, I can list the Commodities with mid quantity for TRADER2
When I make a GET request to /api/queries/selectCommoditiesWithMidQuantity?owner=resource%3Aorg.acme.biznet.Trader%23TRADER2
Then The response code should be 200
And The response body should be JSON matching
"""
[
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
}
]
"""

Scenario: Using the REST API, I can list the Commodities with mid quantity for TRADER1
When I make a GET request to /api/queries/selectCommoditiesWithMidQuantity?owner=resource%3Aorg.acme.biznet.Trader%23TRADER1
Then The response code should be 200
And The response body should be JSON matching
"""
[]
"""

Scenario: Using the REST API, I can remove the Commodities with high quantity
When I make a POST request to /api/RemoveHighQuantityCommodities
"""
Expand All @@ -284,6 +366,14 @@ Feature: Queries steps
And The response body should be JSON matching
"""
[
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "BUT",
"description": "Butter",
"mainExchange": "ICE",
"quantity": 50,
"owner": "resource:org.acme.biznet.Trader#TRADER2"
},
{
"$class": "org.acme.biznet.Commodity",
"tradingSymbol": "EMA",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ query selectCommoditiesOrderedReverse {
SELECT org.acme.biznet.Commodity
ORDER BY [quantity DESC]
}

query selectCommoditiesWithMidQuantity {
description: "Select commodities based on quantity and owner"
statement:
SELECT org.acme.biznet.Commodity
WHERE (quantity >= 30 AND quantity < 60 AND owner == _$owner)
}

0 comments on commit 73dd7d7

Please sign in to comment.