Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ghost-gql

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ghost-gql - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

.editorconfig

31

dist/parser.js

@@ -1,2 +0,2 @@

/* parser generated by jison 0.4.15 */
/* parser generated by jison 0.4.16 */
/*

@@ -169,3 +169,9 @@ Returns a Parser object of the following structure:

} else {
throw new Error(str);
function _parseError (msg, hash) {
this.message = msg;
this.hash = hash;
}
_parseError.prototype = new Error();
throw new _parseError(str, hash);
}

@@ -203,3 +209,3 @@ },

_token_stack:
function lex() {
var lex = function () {
var token;

@@ -211,3 +217,3 @@ token = lexer.lex() || EOF;

return token;
}
};
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;

@@ -318,2 +324,8 @@ while (true) {

// console.log("parser object definition: ", this);
parser.parseError = function(errStr, object) {
var lines = errStr.split("\n");
lines[0] = "Query Error: unexpected character in filter at char " + (object.loc.first_column + 1);
throw new Error(lines.join("\n"));
};
/* generated by jison-lex 0.3.4 */

@@ -690,2 +702,13 @@ var lexer = (function(){

});
lexer.parseError = function(errStr, object) {
var lines = errStr.split("\n"),
caretPos,
char;
caretPos = lines[2].indexOf("^");
char = lines[1].charAt(caretPos);
lines[0] = "Query Error: unrecognized text \"" + char + "\" in filter at char " + (caretPos + 1);
throw Error(lines.join("\n"));
};
return lexer;

@@ -692,0 +715,0 @@ })();

2

lib/context.js

@@ -26,2 +26,2 @@ /**

module.exports = resourceContext;
module.exports = resourceContext;
var parser = require('../dist/parser').parser;
parser.yy = require("./scope");
parser.yy = require('./scope');
var lex = exports.lex = function (input) {
exports.lex = function (input) {
parser.lexer.setInput(input);

@@ -18,4 +18,4 @@ var lexed = parser.lexer.lex(),

// returns the JSON object
var parse = exports.parse = function (input, resource_type, aliases) {
return parser.parse(input, resource_type, aliases);
exports.parse = function (input, resourceType, aliases) {
return parser.parse(input, resourceType, aliases);
};

@@ -22,0 +22,0 @@

@@ -59,5 +59,5 @@ /**

addJoin(parts[0]);
//if (context.relations && context.relations.indexOf(parts[parts.length - 1]) > -1) {
// addJoin(path);
//}
// if (context.relations && context.relations.indexOf(parts[parts.length - 1]) > -1) {
// addJoin(path);
// }
}

@@ -80,4 +80,4 @@

* Detect Where Type
* @param statement
* @param index
* @param {Object} statement
* @param {int} index
* @returns {string}

@@ -89,3 +89,2 @@ */

whereFunc = 'where';
} else if (statement.func === 'or') {

@@ -109,4 +108,4 @@ whereFunc = 'orWhere';

*
* @param qb
* @param statements
* @param {Object} qb
* @param {Array} statements
* @returns {*}

@@ -134,4 +133,4 @@ */

*
* @param qb
* @param filter
* @param {Object} qb
* @param {Array} filter
* @returns {object} queryBuilder

@@ -147,2 +146,2 @@ */

// For testing only
module.exports._buildWhere = buildWhere;
module.exports._buildWhere = buildWhere;

@@ -9,3 +9,2 @@ /**

var _ = require('lodash'),
matchStatement,

@@ -39,3 +38,3 @@ eachStatement,

}
})
});
};

@@ -53,3 +52,3 @@

* @param {Function} [groupFunc] - optionally provide a function to call for groups instead of automatic recursion
*/
*/
eachStatement = function eachStatement(statements, func, groupFunc) {

@@ -126,4 +125,4 @@ _.each(statements, function (statement, index) {

if (result && index === 0 && statements[index+1] && statements[index+1].func) {
delete statements[index+1].func;
if (result && index === 0 && statements[index + 1] && statements[index + 1].func) {
delete statements[index + 1].func;
}

@@ -194,3 +193,3 @@ return result;

function print(indent /* strs */) {
var strs = [_.fill(Array(indent), ' ').join('')].concat(Array.prototype.slice.call(arguments, 1));
var strs = [_.fill(new Array(indent), ' ').join('')].concat(Array.prototype.slice.call(arguments, 1));
console.log.apply(this, strs);

@@ -213,2 +212,2 @@ }

printStatements: printStatements
};
};
var scope = {};
scope.resolveOp = function(op, value) {
scope.resolveOp = function (op, value) {
if (value === null) {

@@ -10,3 +10,3 @@ return op === '!=' ? 'IS NOT' : 'IS';

scope.unescape = function(value) {
scope.unescape = function (value) {
var re = new RegExp('\\\\([\'"])', 'g');

@@ -16,3 +16,2 @@ return value.replace(re, '$1');

module.exports = scope;
module.exports = scope;
{
"name": "ghost-gql",
"version": "0.0.3",
"version": "0.0.4",
"description": "Filter query language for Ghost",
"main": "index.js",
"scripts": {
"test": "mocha",
"build": "grunt build"
"lint": "jshint lib/*.js test/*.js && jscs lib/*.js test/*.js",
"test": "npm run build && npm run lint && mocha",
"build": "jison src/gql.y src/gql.l -o dist/parser.js",
"coverage": "istanbul cover -x src --dir=test/coverage --report=lcov ./node_modules/mocha/bin/_mocha -- test/*_spec.js",
"release": "npm-release patch"
},

@@ -26,8 +29,9 @@ "author": "Ghost Foundation",

"devDependencies": {
"grunt": "0.4.5",
"grunt-mocha-istanbul": "2.4.0",
"grunt-shell": "1.1.2",
"jison": "0.4.15",
"istanbul": "0.4.0",
"jison": "0.4.16",
"jscs": "2.8.0",
"jshint": "2.8.0",
"knex": "0.8.6",
"mocha": "2.2.5",
"npm-release": "1.0.0",
"should": "7.0.1",

@@ -34,0 +38,0 @@ "sinon": "1.15.4"

# GQL
GQL is a filter query language for working with Ghost's JSON API
GQL stands for 'Ghost Query Language'
It allows for parsing filter strings of the form `featured:true+tags.count:>10`, `tags:photo,tags:video`, etc
The aim is to provide a simple gmail or github filter-like syntax for specifying conditions, whilst being flexible and powerful enough to support the majority of 'where' expressions available in SQL.
The full spec can be found in <https://github.com/TryGhost/Ghost/issues/5604>
GQL itself is parsed and expanded out into a JSON object which can be used to build queries in SQL (and probably No SQL).
### Example:
The GQL expression `featured:true+tags.count:>10`
Would be converted to the following JSON object:
```
{statements: [
{prop: "featured", op: "=", value: true},
{prop: "tags.count", op: ">", value: 10, func: "and"}
]}
```
And via Knex, would be further converted to the following SQL:
`where "featured" = true and "tags"."count" > 10`
Inside of Ghost, this syntax is accepted via the `filter` parameter when browsing resources in our JSON API.
## What's in the box?
This repository comes in three parts:
- the language parsing functionality, providing `gql.parse()`
- a set of lodash-like tools for processing the JSON objects returned
- some currently Ghost-specific helpers for converting the JSON objects into SQL via [knex's query builder](http://knexjs.org/)
The intention is to eventually move all of the Ghost-specific code and replace it with generic query-building code for Knex and perhaps also a bookshelf plugin. It should also be possible to provide other interfaces, e.g. a direct conversion to SQL or NoSQL query formats.
## Usage
Knex:
```
var filters = gql.parse('featured:true+tags.count:>10');
gql.knexify(knex('myTable'), filters);
```
Bookshelf:
```
var filters = gql.parse('featured:true+tags.count:>10');
myBookshelfModel.forge().query(function (qb) {
gql.knexify(qb, filters);
});
```
To get raw SQL via Knex:
```
var filters = gql.parse('featured:true+tags.count:>10');
var myTable = knex('myTable');
gql.knexify(myTable, filters);
return myTable.toQuery();
```
### Statement processing
GQL also supported grouped statements, e.g. `author:joe+(tag:photo,image:-null)`
Which result in nested statements like this:
```
{statements: [
{op: "!=", value: "joe", prop: "author"},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "or"}
], func: "and"}
]}
```
And which should result in the following SQL:
`where "author"."slug" != "joe" and ("posts"."featured" = true or "posts"."image" is not null);`
As the JSON returned by GQL is not always a simple set of objects, performing an operation on every statement requires a recursive loop. GQL provides tools for this:
* eachStatement
* findStatement
* matchStatement
* mergeStatements
* rejectStatements
* printStatements
There are currently two ways that you 'could' use these functions externally (e.g. in Ghost) and in the vein of naming things is hard, I can't decide which I prefer.
You could do:
```
var _ = require('lodash');
_.mixin(require('ghost-gql').json);
_.eachStatement(statements...);
```
Or you could do
```
var gql = require('ghost-gql');
gql.json.eachStatement(statements...);
```
For now you'll need to use the [inline docs](https://github.com/TryGhost/GQL/blob/master/lib/lodash-stmt.js#L10) which explain how to use each function.
## Syntax
The full spec can be found in <https://github.com/TryGhost/Ghost/issues/5604> - I will move this eventually.
## How and why
GQL exists because we needed a very simple filter syntax that could be passed as a string in either a method call, a URL, or a handlebars helper attribute. The concept was originally proposed in https://github.com/TryGhost/Ghost/issues/5463#user-content-advancedfiltering and then later spec'd more fully in https://github.com/TryGhost/Ghost/issues/5604. The syntax created works well no matter whether the API is being called internally or externally.
The two-step conversion process from GQL -> JSON -> SQL exists for flexibility. This library can and will handle the whole process, but with the JSON step in the middle and the lodash style tools for processing the JSON, it is possible to perform various operations on the JSON, for example, filtering out unsafe conditions.
Also it's possible to implement conversion from the JSON format to SQL either via knex or without it, as well as to no-SQL JSON-like query formats.
The conversion from GQL -> JSON is performed via a [JISON](http://zaach.github.io/jison/) parser. [JISON](http://zaach.github.io/jison/) is an amazing tool that allows you to easily specify the rules for a language in a JavaScript like syntax, and it creates the parser for you.
In the `/src/` folder is a .l and a .y file used by JISON to generate the parser. `gql.l` is the lexer or tokenizer that defines all of the symbols that GQL can understand. `gql.y` is the grammar, it defines the rules about in what order the symbols must appear. If you make changes to `gql.l` or `gql.y`, you'll need to run `grunt build` in order to generate a new version of the parser in `/dist/`.

@@ -1,6 +0,7 @@

var should = require('should'),
gql = require('../lib/gql'),
knex = require('knex')({});
/* globals describe, it */
var gql = require('../lib/gql'),
knex = require('knex')({}),
toSQL;
var toSQL = exports.toSQL = function (input, resource) {
toSQL = exports.toSQL = function (input, resource) {
var parsedFilter = gql.parse(input);

@@ -12,9 +13,9 @@ return gql.knexify(knex(resource), parsedFilter).toQuery();

it('should correctly get from GQL -> SQL', function () {
toSQL("id:1", 'posts').should.eql('select * from "posts" where "posts"."id" = 1');
toSQL('id:1', 'posts').should.eql('select * from "posts" where "posts"."id" = 1');
});
it('should correctly escape bad sequences', function () {
(function () {toSQL("id:'1 and 1‘=\'1`'", 'posts')}).should.throw();
toSQL("id:'1 and 1‘=\\\'1`'", 'posts').should.eql('select * from "posts" where "posts"."id" = \'1 and 1‘=\\\'1`\'');
(function () {toSQL('id:\'1 and 1‘=\'1`\'', 'posts');}).should.throw();
toSQL('id:\'1 and 1‘=\\\'1`\'', 'posts').should.eql('select * from "posts" where "posts"."id" = \'1 and 1‘=\\\'1`\'');
});
});

@@ -1,3 +0,3 @@

var should = require('should'),
sinon = require('sinon'),
/* globals describe, beforeEach, afterEach, it */
var sinon = require('sinon'),
knex = require('knex')({}),

@@ -263,8 +263,8 @@ knexify = require('../lib/knexify');

statements: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'
}

@@ -300,8 +300,8 @@ ]

statements: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "true", prop: "featured"},
{op: '=', value: 'true', prop: 'featured'},
{prop: 'image', op: 'IS NOT', value: null, func: 'or'}
], func: "and"
], func: 'and'
}

@@ -308,0 +308,0 @@ ]

@@ -1,52 +0,52 @@

var should = require('should'),
gql = require('../lib/gql');
/* globals describe, it */
var gql = require('../lib/gql');
describe('Lexer', function () {
var lexicalError = /^Lexical error on line 1\. Unrecognized text\./;
var lexicalError = /^Query Error: unrecognized text/;
describe('Symbols', function () {
it('can recognise -', function () {
gql.lex("-").should.eql([{token: "NOT", matched: "-"}]);
gql.lex('-').should.eql([{token: 'NOT', matched: '-'}]);
});
it('can recognise +', function () {
gql.lex("+").should.eql([{token: "AND", matched: "+"}]);
gql.lex('+').should.eql([{token: 'AND', matched: '+'}]);
});
it('can recognise ,', function () {
gql.lex(",").should.eql([{token: "OR", matched: ","}]);
gql.lex(',').should.eql([{token: 'OR', matched: ','}]);
});
it('can recognise [', function () {
gql.lex("[").should.eql([{token: "LBRACKET", matched: "["}]);
gql.lex('[').should.eql([{token: 'LBRACKET', matched: '['}]);
});
it('can recognise ]', function () {
gql.lex("]").should.eql([{token: "RBRACKET", matched: "]"}]);
gql.lex(']').should.eql([{token: 'RBRACKET', matched: ']'}]);
});
it('can recognise (', function () {
gql.lex("(").should.eql([{token: "LPAREN", matched: "("}]);
gql.lex('(').should.eql([{token: 'LPAREN', matched: '('}]);
});
it('can recognise )', function () {
gql.lex(")").should.eql([{token: "RPAREN", matched: ")"}]);
gql.lex(')').should.eql([{token: 'RPAREN', matched: ')'}]);
});
it('can recognise >', function () {
gql.lex(">").should.eql([{token: "GT", matched: ">"}]);
gql.lex('>').should.eql([{token: 'GT', matched: '>'}]);
});
it('can recognise <', function () {
gql.lex("<").should.eql([{token: "LT", matched: "<"}]);
gql.lex('<').should.eql([{token: 'LT', matched: '<'}]);
});
it('can recognise >=', function () {
gql.lex(">=").should.eql([{token: "GTE", matched: ">="}]);
gql.lex('>=').should.eql([{token: 'GTE', matched: '>='}]);
});
it('can recognise <=', function () {
gql.lex("<=").should.eql([{token: "LTE", matched: "<="}]);
gql.lex('<=').should.eql([{token: 'LTE', matched: '<='}]);
});
it('cannot recognise :', function () {
(function () {gql.lex(":")}).should.throw(lexicalError);
(function () {gql.lex(':');}).should.throw(lexicalError);
});
it('cannot recognise =', function () {
(function () {gql.lex("=")}).should.throw(lexicalError);
(function () {gql.lex('=');}).should.throw(lexicalError);
});
it('cannot recognise "', function () {
(function () {gql.lex('"')}).should.throw(lexicalError);
(function () {gql.lex('"');}).should.throw(lexicalError);
});
it('cannot recognise \'', function () {
(function () {gql.lex("'")}).should.throw(lexicalError);
(function () {gql.lex('\'');}).should.throw(lexicalError);
});

@@ -57,37 +57,37 @@ });

it('can recognise null', function () {
gql.lex("null").should.eql([{token: "NULL", matched: "null"}]);
gql.lex('null').should.eql([{token: 'NULL', matched: 'null'}]);
});
it('can recognise true', function () {
gql.lex("true").should.eql([{token: "TRUE", matched: "true"}]);
gql.lex('true').should.eql([{token: 'TRUE', matched: 'true'}]);
});
it('can recognise false', function () {
gql.lex("false").should.eql([{token: "FALSE", matched: "false"}]);
gql.lex('false').should.eql([{token: 'FALSE', matched: 'false'}]);
});
it('can recognise a LITERAL', function () {
gql.lex("six").should.eql([{token: "LITERAL", matched: "six"}]);
gql.lex('six').should.eql([{token: 'LITERAL', matched: 'six'}]);
});
it('can recognise a STRING', function () {
gql.lex("'six'").should.eql([{token: "STRING", matched: "'six'"}]);
gql.lex('\'six\'').should.eql([{token: 'STRING', matched: '\'six\''}]);
});
it('can recognise a NUMBER', function () {
gql.lex("6").should.eql([{token: "NUMBER", matched: "6"}]);
gql.lex('6').should.eql([{token: 'NUMBER', matched: '6'}]);
});
it('does not confuse values in LITERALs', function () {
gql.lex("strueth").should.eql([{token: "LITERAL", matched: "strueth"}]);
gql.lex("trueth").should.eql([{token: "LITERAL", matched: "trueth"}]);
gql.lex("true_thing").should.eql([{token: "LITERAL", matched: "true_thing"}]);
//gql.lex("true-thing").should.eql([{token: "LITERAL", matched: "true-thing"}]);
gql.lex('strueth').should.eql([{token: 'LITERAL', matched: 'strueth'}]);
gql.lex('trueth').should.eql([{token: 'LITERAL', matched: 'trueth'}]);
gql.lex('true_thing').should.eql([{token: 'LITERAL', matched: 'true_thing'}]);
// gql.lex("true-thing").should.eql([{token: "LITERAL", matched: "true-thing"}]);
});
it('does not confuse values in STRINGs', function () {
gql.lex("'strueth'").should.eql([{token: "STRING", matched: "'strueth'"}]);
gql.lex("'trueth'").should.eql([{token: "STRING", matched: "'trueth'"}]);
gql.lex("'true_thing'").should.eql([{token: "STRING", matched: "'true_thing'"}]);
gql.lex("'true-thing'").should.eql([{token: "STRING", matched: "'true-thing'"}]);
gql.lex('\'strueth\'').should.eql([{token: 'STRING', matched: '\'strueth\''}]);
gql.lex('\'trueth\'').should.eql([{token: 'STRING', matched: '\'trueth\''}]);
gql.lex('\'true_thing\'').should.eql([{token: 'STRING', matched: '\'true_thing\''}]);
gql.lex('\'true-thing\'').should.eql([{token: 'STRING', matched: '\'true-thing\''}]);
});

@@ -98,30 +98,30 @@ });

it('should match literals', function () {
gql.lex("myvalue").should.eql([
{token: "LITERAL", matched: "myvalue"}
gql.lex('myvalue').should.eql([
{token: 'LITERAL', matched: 'myvalue'}
]);
gql.lex("my value").should.eql([
{token: "LITERAL", matched: "my"},
{token: "LITERAL", matched: "value"}
gql.lex('my value').should.eql([
{token: 'LITERAL', matched: 'my'},
{token: 'LITERAL', matched: 'value'}
]);
gql.lex("my-value").should.eql([
{token: "LITERAL", matched: "my-value"}
gql.lex('my-value').should.eql([
{token: 'LITERAL', matched: 'my-value'}
]);
gql.lex("my&value!").should.eql([
{token: "LITERAL", matched: "my&value!"}
gql.lex('my&value!').should.eql([
{token: 'LITERAL', matched: 'my&value!'}
]);
gql.lex("my&valu\\'e!").should.eql([
{token: "LITERAL", matched: "my&valu\\'e!"}
gql.lex('my&valu\\\'e!').should.eql([
{token: 'LITERAL', matched: 'my&valu\\\'e!'}
]);
(function() {gql.lex("my&valu'e!")}).should.throw(lexicalError);
(function () {gql.lex('my&valu\'e!');}).should.throw(lexicalError);
});
it('should separate NOT at beginning of literal', function () {
gql.lex("-photo").should.eql([
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "photo"}
gql.lex('-photo').should.eql([
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'photo'}
]);
gql.lex('-photo-graph').should.eql([
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "photo-graph"}
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'photo-graph'}
]);

@@ -131,120 +131,120 @@ });

it('should NOT permit special chars inside a literal', function () {
(function () { gql.lex("t+st") }).should.throw(lexicalError);
(function () { gql.lex("t,st") }).should.throw(lexicalError);
(function () { gql.lex("t(st") }).should.throw(lexicalError);
(function () { gql.lex("t)st") }).should.throw(lexicalError);
(function () { gql.lex("t>st") }).should.throw(lexicalError);
(function () { gql.lex("t<st") }).should.throw(lexicalError);
(function () { gql.lex("t=st") }).should.throw(lexicalError);
(function () { gql.lex("t[st") }).should.throw(lexicalError);
(function () { gql.lex("t]st") }).should.throw(lexicalError);
(function () { gql.lex("t'st") }).should.throw(lexicalError);
(function () { gql.lex('t"st') }).should.throw(lexicalError);
(function () { gql.lex('t+st');}).should.throw(lexicalError);
(function () { gql.lex('t,st');}).should.throw(lexicalError);
(function () { gql.lex('t(st');}).should.throw(lexicalError);
(function () { gql.lex('t)st');}).should.throw(lexicalError);
(function () { gql.lex('t>st');}).should.throw(lexicalError);
(function () { gql.lex('t<st');}).should.throw(lexicalError);
(function () { gql.lex('t=st');}).should.throw(lexicalError);
(function () { gql.lex('t[st');}).should.throw(lexicalError);
(function () { gql.lex('t]st');}).should.throw(lexicalError);
(function () { gql.lex('t\'st');}).should.throw(lexicalError);
(function () { gql.lex('t"st');}).should.throw(lexicalError);
});
it('should not match special chars at the start of a literal', function () {
gql.lex("+test").should.eql([
{token: "AND", matched: "+"},
{token: "LITERAL", matched: "test"}
gql.lex('+test').should.eql([
{token: 'AND', matched: '+'},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex(",test").should.eql([
{token: "OR", matched: ","},
{token: "LITERAL", matched: "test"}
gql.lex(',test').should.eql([
{token: 'OR', matched: ','},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex("(test").should.eql([
{token: "LPAREN", matched: "("},
{token: "LITERAL", matched: "test"}
gql.lex('(test').should.eql([
{token: 'LPAREN', matched: '('},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex(")test").should.eql([
{token: "RPAREN", matched: ")"},
{token: "LITERAL", matched: "test"}
gql.lex(')test').should.eql([
{token: 'RPAREN', matched: ')'},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex(">test").should.eql([
{token: "GT", matched: ">"},
{token: "LITERAL", matched: "test"}
gql.lex('>test').should.eql([
{token: 'GT', matched: '>'},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex("<test").should.eql([
{token: "LT", matched: "<"},
{token: "LITERAL", matched: "test"}
gql.lex('<test').should.eql([
{token: 'LT', matched: '<'},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex("[test").should.eql([
{token: "LBRACKET", matched: "["},
{token: "LITERAL", matched: "test"}
gql.lex('[test').should.eql([
{token: 'LBRACKET', matched: '['},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex("]test").should.eql([
{token: "RBRACKET", matched: "]"},
{token: "LITERAL", matched: "test"}
gql.lex(']test').should.eql([
{token: 'RBRACKET', matched: ']'},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex(">=test").should.eql([
{token: "GTE", matched: ">="},
{token: "LITERAL", matched: "test"}
gql.lex('>=test').should.eql([
{token: 'GTE', matched: '>='},
{token: 'LITERAL', matched: 'test'}
]);
gql.lex("<=test").should.eql([
{token: "LTE", matched: "<="},
{token: "LITERAL", matched: "test"}
gql.lex('<=test').should.eql([
{token: 'LTE', matched: '<='},
{token: 'LITERAL', matched: 'test'}
]);
(function () { gql.lex("=test") }).should.throw(lexicalError);
(function () { gql.lex('"test') }).should.throw(lexicalError);
(function () { gql.lex("'test") }).should.throw(lexicalError);
(function () { gql.lex('=test');}).should.throw(lexicalError);
(function () { gql.lex('"test');}).should.throw(lexicalError);
(function () { gql.lex('\'test');}).should.throw(lexicalError);
});
it('should not match special chars at the end of a literal', function () {
gql.lex("test+").should.eql([
{token: "LITERAL", matched: "test"},
{token: "AND", matched: "+"}
gql.lex('test+').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'AND', matched: '+'}
]);
gql.lex("test,").should.eql([
{token: "LITERAL", matched: "test"},
{token: "OR", matched: ","}
gql.lex('test,').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'OR', matched: ','}
]);
gql.lex("test(").should.eql([
{token: "LITERAL", matched: "test"},
{token: "LPAREN", matched: "("}
gql.lex('test(').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'LPAREN', matched: '('}
]);
gql.lex("test)").should.eql([
{token: "LITERAL", matched: "test"},
{token: "RPAREN", matched: ")"}
gql.lex('test)').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'RPAREN', matched: ')'}
]);
gql.lex("test>").should.eql([
{token: "LITERAL", matched: "test"},
{token: "GT", matched: ">"}
gql.lex('test>').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'GT', matched: '>'}
]);
gql.lex("test<").should.eql([
{token: "LITERAL", matched: "test"},
{token: "LT", matched: "<"}
gql.lex('test<').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'LT', matched: '<'}
]);
gql.lex("test[").should.eql([
{token: "LITERAL", matched: "test"},
{token: "LBRACKET", matched: "["}
gql.lex('test[').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'LBRACKET', matched: '['}
]);
gql.lex("test]").should.eql([
{token: "LITERAL", matched: "test"},
{token: "RBRACKET", matched: "]"}
gql.lex('test]').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'RBRACKET', matched: ']'}
]);
gql.lex("test>=").should.eql([
{token: "LITERAL", matched: "test"},
{token: "GTE", matched: ">="}
gql.lex('test>=').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'GTE', matched: '>='}
]);
gql.lex("test<=").should.eql([
{token: "LITERAL", matched: "test"},
{token: "LTE", matched: "<="}
gql.lex('test<=').should.eql([
{token: 'LITERAL', matched: 'test'},
{token: 'LTE', matched: '<='}
]);
(function () { gql.lex("test=") }).should.throw(lexicalError);
(function () { gql.lex('test"') }).should.throw(lexicalError);
(function () { gql.lex("test'") }).should.throw(lexicalError);
(function () { gql.lex('test=');}).should.throw(lexicalError);
(function () { gql.lex('test"');}).should.throw(lexicalError);
(function () { gql.lex('test\'');}).should.throw(lexicalError);
});
it('should permit escaped special chars inside a literal', function () {
gql.lex("t\\+st").should.eql([{token: "LITERAL", matched: "t\\+st"}]);
gql.lex("t\\,st").should.eql([{token: "LITERAL", matched: "t\\,st"}]);
gql.lex("t\\(st").should.eql([{token: "LITERAL", matched: "t\\(st"}]);
gql.lex("t\\)st").should.eql([{token: "LITERAL", matched: "t\\)st"}]);
gql.lex("t\\>st").should.eql([{token: "LITERAL", matched: "t\\>st"}]);
gql.lex("t\\<st").should.eql([{token: "LITERAL", matched: "t\\<st"}]);
gql.lex("t\\=st").should.eql([{token: "LITERAL", matched: "t\\=st"}]);
gql.lex("t\\[st").should.eql([{token: "LITERAL", matched: "t\\[st"}]);
gql.lex("t\\]st").should.eql([{token: "LITERAL", matched: "t\\]st"}]);
gql.lex("t\\'st").should.eql([{token: "LITERAL", matched: "t\\'st"}]);
gql.lex('t\\"st').should.eql([{token: "LITERAL", matched: 't\\"st'}]);
gql.lex('t\\+st').should.eql([{token: 'LITERAL', matched: 't\\+st'}]);
gql.lex('t\\,st').should.eql([{token: 'LITERAL', matched: 't\\,st'}]);
gql.lex('t\\(st').should.eql([{token: 'LITERAL', matched: 't\\(st'}]);
gql.lex('t\\)st').should.eql([{token: 'LITERAL', matched: 't\\)st'}]);
gql.lex('t\\>st').should.eql([{token: 'LITERAL', matched: 't\\>st'}]);
gql.lex('t\\<st').should.eql([{token: 'LITERAL', matched: 't\\<st'}]);
gql.lex('t\\=st').should.eql([{token: 'LITERAL', matched: 't\\=st'}]);
gql.lex('t\\[st').should.eql([{token: 'LITERAL', matched: 't\\[st'}]);
gql.lex('t\\]st').should.eql([{token: 'LITERAL', matched: 't\\]st'}]);
gql.lex('t\\\'st').should.eql([{token: 'LITERAL', matched: 't\\\'st'}]);
gql.lex('t\\"st').should.eql([{token: 'LITERAL', matched: 't\\"st'}]);
});

@@ -255,30 +255,29 @@ });

it('should match colon in string as PROP before, literal after', function () {
gql.lex(":test").should.eql([
{token: "LITERAL", matched: ":test"}
gql.lex(':test').should.eql([
{token: 'LITERAL', matched: ':test'}
]);
gql.lex("te:st").should.eql([
{token: "PROP", matched: "te:"},
{token: "LITERAL", matched: "st"}
gql.lex('te:st').should.eql([
{token: 'PROP', matched: 'te:'},
{token: 'LITERAL', matched: 'st'}
]);
gql.lex("test:").should.eql([
{token: "PROP", matched: "test:"}
gql.lex('test:').should.eql([
{token: 'PROP', matched: 'test:'}
]);
});
it('should only match colon-at-end as PROP if PROP is valPROP', function() {
gql.lex("te!:st").should.eql([
{token: "LITERAL", matched: "te!:st"}
it('should only match colon-at-end as PROP if PROP is valPROP', function () {
gql.lex('te!:st').should.eql([
{token: 'LITERAL', matched: 'te!:st'}
]);
gql.lex("post-count:6").should.eql([
{token: "LITERAL", matched: "post-count:6"}
gql.lex('post-count:6').should.eql([
{token: 'LITERAL', matched: 'post-count:6'}
]);
gql.lex("post_count:6").should.eql([
{token: "PROP", matched: "post_count:"},
{token: "NUMBER", matched: "6"}
gql.lex('post_count:6').should.eql([
{token: 'PROP', matched: 'post_count:'},
{token: 'NUMBER', matched: '6'}
]);
});

@@ -289,27 +288,27 @@ });

it('can recognise simple STRING', function () {
gql.lex("'magic'").should.eql([{token: "STRING", matched: "'magic'"}]);
gql.lex("'magic mystery'").should.eql([{token: "STRING", matched: "'magic mystery'"}]);
gql.lex("'magic 123'").should.eql([{token: "STRING", matched: "'magic 123'"}]);
gql.lex('\'magic\'').should.eql([{token: 'STRING', matched: '\'magic\''}]);
gql.lex('\'magic mystery\'').should.eql([{token: 'STRING', matched: '\'magic mystery\''}]);
gql.lex('\'magic 123\'').should.eql([{token: 'STRING', matched: '\'magic 123\''}]);
});
it('can recognise multiple STRING values', function () {
gql.lex("'magic''mystery'").should.eql([
{token: "STRING", matched: "'magic'"},
{token: "STRING", matched: "'mystery'"}
gql.lex('\'magic\'\'mystery\'').should.eql([
{token: 'STRING', matched: '\'magic\''},
{token: 'STRING', matched: '\'mystery\''}
]);
gql.lex("'magic' 'mystery'").should.eql([
{token: "STRING", matched: "'magic'"},
{token: "STRING", matched: "'mystery'"}
gql.lex('\'magic\' \'mystery\'').should.eql([
{token: 'STRING', matched: '\'magic\''},
{token: 'STRING', matched: '\'mystery\''}
]);
gql.lex("'magic','mystery'").should.eql([
{token: "STRING", matched: "'magic'"},
{token: "OR", matched: ","},
{token: "STRING", matched: "'mystery'"}
gql.lex('\'magic\',\'mystery\'').should.eql([
{token: 'STRING', matched: '\'magic\''},
{token: 'OR', matched: ','},
{token: 'STRING', matched: '\'mystery\''}
]);
gql.lex("['magic','mystery']").should.eql([
{token: "LBRACKET", matched: "["},
{token: "STRING", matched: "'magic'"},
{token: "OR", matched: ","},
{token: "STRING", matched: "'mystery'"},
{token: "RBRACKET", matched: "]"}
gql.lex('[\'magic\',\'mystery\']').should.eql([
{token: 'LBRACKET', matched: '['},
{token: 'STRING', matched: '\'magic\''},
{token: 'OR', matched: ','},
{token: 'STRING', matched: '\'mystery\''},
{token: 'RBRACKET', matched: ']'}
]);

@@ -319,30 +318,29 @@ });

it('can recognise STRING with special characters', function () {
gql.lex("'magic+'").should.eql([{token: "STRING", matched: "'magic+'"}]);
gql.lex("'magic,'").should.eql([{token: "STRING", matched: "'magic,'"}]);
gql.lex("'magic-'").should.eql([{token: "STRING", matched: "'magic-'"}]);
gql.lex("'magic>'").should.eql([{token: "STRING", matched: "'magic>'"}]);
gql.lex("'magic<'").should.eql([{token: "STRING", matched: "'magic<'"}]);
gql.lex('\'magic+\'').should.eql([{token: 'STRING', matched: '\'magic+\''}]);
gql.lex('\'magic,\'').should.eql([{token: 'STRING', matched: '\'magic,\''}]);
gql.lex('\'magic-\'').should.eql([{token: 'STRING', matched: '\'magic-\''}]);
gql.lex('\'magic>\'').should.eql([{token: 'STRING', matched: '\'magic>\''}]);
gql.lex('\'magic<\'').should.eql([{token: 'STRING', matched: '\'magic<\''}]);
});
it('should permit special chars inside a STRING, not including quotes', function () {
gql.lex("'t+st'").should.eql([{token: "STRING", matched: "'t+st'"}]);
gql.lex("'t,st'").should.eql([{token: "STRING", matched: "'t,st'"}]);
gql.lex("'t(st'").should.eql([{token: "STRING", matched: "'t(st'"}]);
gql.lex("'t)st'").should.eql([{token: "STRING", matched: "'t)st'"}]);
gql.lex("'t>st'").should.eql([{token: "STRING", matched: "'t>st'"}]);
gql.lex("'t<st'").should.eql([{token: "STRING", matched: "'t<st'"}]);
gql.lex("'t=st'").should.eql([{token: "STRING", matched: "'t=st'"}]);
gql.lex("'t[st'").should.eql([{token: "STRING", matched: "'t[st'"}]);
gql.lex("'t]st'").should.eql([{token: "STRING", matched: "'t]st'"}]);
gql.lex('\'t+st\'').should.eql([{token: 'STRING', matched: '\'t+st\''}]);
gql.lex('\'t,st\'').should.eql([{token: 'STRING', matched: '\'t,st\''}]);
gql.lex('\'t(st\'').should.eql([{token: 'STRING', matched: '\'t(st\''}]);
gql.lex('\'t)st\'').should.eql([{token: 'STRING', matched: '\'t)st\''}]);
gql.lex('\'t>st\'').should.eql([{token: 'STRING', matched: '\'t>st\''}]);
gql.lex('\'t<st\'').should.eql([{token: 'STRING', matched: '\'t<st\''}]);
gql.lex('\'t=st\'').should.eql([{token: 'STRING', matched: '\'t=st\''}]);
gql.lex('\'t[st\'').should.eql([{token: 'STRING', matched: '\'t[st\''}]);
gql.lex('\'t]st\'').should.eql([{token: 'STRING', matched: '\'t]st\''}]);
});
it('should NOT permit quotes inside a STRING', function () {
(function () { gql.lex("'t'st'") }).should.throw(lexicalError);
(function () { gql.lex("'t\"st'") }).should.throw(lexicalError);
(function () { gql.lex('\'t\'st\'');}).should.throw(lexicalError);
(function () { gql.lex('\'t"st\'');}).should.throw(lexicalError);
});
it('should permit escaped quotes inside a String', function () {
gql.lex("'t\\'st'").should.eql([{token: "STRING", matched: "'t\\'st'"}]);
gql.lex("'t\\\"st'").should.eql([{token: "STRING", matched: "'t\\\"st'"}]);
gql.lex('\'t\\\'st\'').should.eql([{token: 'STRING', matched: '\'t\\\'st\''}]);
gql.lex('\'t\\"st\'').should.eql([{token: 'STRING', matched: '\'t\\"st\''}]);
});

@@ -353,25 +351,24 @@ });

it('CANNOT match an UNescaped double quote in a LITERAL', function () {
(function () {gql.lex('thing"amabob')}).should.throw(lexicalError);
(function () {gql.lex('thing"amabob');}).should.throw(lexicalError);
});
it('CANNOT match an UNescaped single quote in a LITERAL', function () {
(function () {gql.lex("thing'amabob")}).should.throw(lexicalError);
(function () {gql.lex('thing\'amabob');}).should.throw(lexicalError);
});
it('CANNOT match an UNescaped double quote in a STRING', function () {
(function () {gql.lex("'thing\"amabob'")}).should.throw(lexicalError);
(function () {gql.lex('\'thing"amabob\'');}).should.throw(lexicalError);
});
it('CANNOT match an UNescaped single quote in a STRING', function () {
(function () {gql.lex("'thing'amabob'")}).should.throw(lexicalError);
(function () {gql.lex('\'thing\'amabob\'');}).should.throw(lexicalError);
});
it('CAN match an escaped double quote in a LITERAL', function () {
gql.lex('thing\\"amabob').should.eql([{token: 'LITERAL', matched: 'thing\\"amabob'}])
gql.lex('thing\\"amabob').should.eql([{token: 'LITERAL', matched: 'thing\\"amabob'}]);
});
it('CAN match an escaped single quote in a LITERAL', function () {
gql.lex("thing\\'amabob").should.eql([{token: 'LITERAL', matched: "thing\\'amabob"}])
gql.lex('thing\\\'amabob').should.eql([{token: 'LITERAL', matched: 'thing\\\'amabob'}]);
});
it('CAN match an escaped double quote in a STRING', function () {
gql.lex("'thing\\\"amabob'").should.eql([{token: 'STRING', matched: "'thing\\\"amabob'"}])
gql.lex('\'thing\\"amabob\'').should.eql([{token: 'STRING', matched: '\'thing\\"amabob\''}]);
});
it('CAN match an escaped single quote in a STRING', function () {
gql.lex("'thing\\'amabob'").should.eql([{token: 'STRING', matched: "'thing\\'amabob'"}])
gql.lex('\'thing\\\'amabob\'').should.eql([{token: 'STRING', matched: '\'thing\\\'amabob\''}]);
});

@@ -382,20 +379,20 @@ });

it('should separate NOT at beginning of literal', function () {
gql.lex("tag:-photo").should.eql([
{token: "PROP", matched: "tag:"},
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "photo"}
gql.lex('tag:-photo').should.eql([
{token: 'PROP', matched: 'tag:'},
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'photo'}
]);
gql.lex("tag:-photo-graph").should.eql([
{token: "PROP", matched: "tag:"},
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "photo-graph"}
gql.lex('tag:-photo-graph').should.eql([
{token: 'PROP', matched: 'tag:'},
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'photo-graph'}
]);
gql.lex("tags:[-getting-started]").should.eql([
{token: "PROP", matched: "tags:"},
{token: "LBRACKET", matched: "["},
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "getting-started"},
{token: "RBRACKET", matched: "]"}
gql.lex('tags:[-getting-started]').should.eql([
{token: 'PROP', matched: 'tags:'},
{token: 'LBRACKET', matched: '['},
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'getting-started'},
{token: 'RBRACKET', matched: ']'}
]);

@@ -405,31 +402,31 @@ });

it('should permit NOT inside a literal', function () {
gql.lex("tags:getting-started").should.eql([
{ token: "PROP", matched: "tags:" },
{ token: "LITERAL", matched: "getting-started" }
gql.lex('tags:getting-started').should.eql([
{token: 'PROP', matched: 'tags:'},
{token: 'LITERAL', matched: 'getting-started'}
]);
gql.lex("tags:[getting-started]").should.eql([
{ token: "PROP", matched: "tags:" },
{ token: "LBRACKET", matched: "[" },
{ token: "LITERAL", matched: "getting-started" },
{ token: "RBRACKET", matched: "]" }
gql.lex('tags:[getting-started]').should.eql([
{token: 'PROP', matched: 'tags:'},
{token: 'LBRACKET', matched: '['},
{token: 'LITERAL', matched: 'getting-started'},
{token: 'RBRACKET', matched: ']'}
]);
gql.lex("tags:-[getting-started]").should.eql([
{ token: "PROP", matched: "tags:" },
{ token: "NOT", matched: "-" },
{ token: "LBRACKET", matched: "[" },
{ token: "LITERAL", matched: "getting-started" },
{ token: "RBRACKET", matched: "]" }
gql.lex('tags:-[getting-started]').should.eql([
{token: 'PROP', matched: 'tags:'},
{token: 'NOT', matched: '-'},
{token: 'LBRACKET', matched: '['},
{token: 'LITERAL', matched: 'getting-started'},
{token: 'RBRACKET', matched: ']'}
]);
gql.lex("id:-1+tags:[getting-started]").should.eql([
{ token: "PROP", matched: "id:" },
{ token: "NOT", matched: "-" },
{ token: "NUMBER", matched: "1" },
{ token: "AND", matched: "+" },
{ token: "PROP", matched: "tags:" },
{ token: "LBRACKET", matched: "[" },
{ token: "LITERAL", matched: "getting-started" },
{ token: "RBRACKET", matched: "]" }
gql.lex('id:-1+tags:[getting-started]').should.eql([
{token: 'PROP', matched: 'id:'},
{token: 'NOT', matched: '-'},
{token: 'NUMBER', matched: '1'},
{token: 'AND', matched: '+'},
{token: 'PROP', matched: 'tags:'},
{token: 'LBRACKET', matched: '['},
{token: 'LITERAL', matched: 'getting-started'},
{token: 'RBRACKET', matched: ']'}
]);

@@ -441,42 +438,40 @@ });

it('many expressions', function () {
gql.lex("tag:photo+featured:true,tag.count:>5").should.eql([
{token: "PROP", matched: "tag:"},
{token: "LITERAL", matched: "photo"},
{token: "AND", matched: "+" },
{token: "PROP", matched: "featured:"},
{token: "TRUE", matched: "true"},
{token: "OR", matched: "," },
{token: "PROP", matched: "tag.count:"},
{token: "GT", matched: ">"},
{token: "NUMBER", matched: "5"}
gql.lex('tag:photo+featured:true,tag.count:>5').should.eql([
{token: 'PROP', matched: 'tag:'},
{token: 'LITERAL', matched: 'photo'},
{token: 'AND', matched: '+'},
{token: 'PROP', matched: 'featured:'},
{token: 'TRUE', matched: 'true'},
{token: 'OR', matched: ','},
{token: 'PROP', matched: 'tag.count:'},
{token: 'GT', matched: '>'},
{token: 'NUMBER', matched: '5'}
]);
//gql.lex("tag:photo+image:-null,tag.count:>5").should.eql();
// gql.lex("tag:photo+image:-null,tag.count:>5").should.eql();
});
it('grouped expressions', function () {
// gql.lex("author:-joe+(tag:photo,image:-null,featured:true)").should.eql();
// gql.lex("author:-joe+(tag:photo,image:-null,featured:true)").should.eql();
});
it('in expressions', function () {
gql.lex("author:-joe+tag:[photo,video]").should.eql([
{token: "PROP", matched: "author:"},
{token: "NOT", matched: "-"},
{token: "LITERAL", matched: "joe"},
{token: "AND", matched: "+"},
{token: "PROP", matched: "tag:"},
{token: "LBRACKET", matched: "["},
{token: "LITERAL", matched: "photo"},
{token: "OR", matched: ","},
{token: "LITERAL", matched: "video"},
{token: "RBRACKET", matched: "]"}
]);
gql.lex('author:-joe+tag:[photo,video]').should.eql([
{token: 'PROP', matched: 'author:'},
{token: 'NOT', matched: '-'},
{token: 'LITERAL', matched: 'joe'},
{token: 'AND', matched: '+'},
{token: 'PROP', matched: 'tag:'},
{token: 'LBRACKET', matched: '['},
{token: 'LITERAL', matched: 'photo'},
{token: 'OR', matched: ','},
{token: 'LITERAL', matched: 'video'},
{token: 'RBRACKET', matched: ']'}
]);
// gql.lex("author:-joe+tag:-[photo,video]").should.eql();
// gql.lex("author:-joe+tag:-[photo,video]").should.eql();
// gql.lex("author:-joe+tag:[photo,video]+post.count:>5+post.count:<100").should.eql();
// gql.lex("author:-joe+tag:[photo,video]+post.count:>5+post.count:<100").should.eql();
});
});
});

@@ -0,1 +1,2 @@

/* globals describe, beforeEach, afterEach, it */
var should = require('should'),

@@ -5,3 +6,3 @@ sinon = require('sinon'),

sandbox = sinon.sandbox.create();
sandbox = sinon.sandbox.create(),

@@ -87,5 +88,5 @@ lodashStmt = require('../lib/lodash-stmt');

eachStatement([
{op: "!=", value: "joe", prop: "author"},
{op: "=", value: "photo", prop: "tag", func: "and"},
{op: "=", value: "video", prop: "tag", func: "or"}
{op: '!=', value: 'joe', prop: 'author'},
{op: '=', value: 'photo', prop: 'tag', func: 'and'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], single, group);

@@ -101,13 +102,13 @@

eachStatement([
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
], single);
single.callCount.should.eql(3);
single.getCall(0).calledWith({op: "!=", value: "joe", prop: "author"}).should.eql(true);
single.getCall(1).calledWith({op: "=", value: "photo", prop: "tag"}).should.eql(true);
single.getCall(2).calledWith({op: "=", value: "video", prop: "tag", func: "or"}).should.eql(true);
single.getCall(0).calledWith({op: '!=', value: 'joe', prop: 'author'}).should.eql(true);
single.getCall(1).calledWith({op: '=', value: 'photo', prop: 'tag'}).should.eql(true);
single.getCall(2).calledWith({op: '=', value: 'video', prop: 'tag', func: 'or'}).should.eql(true);
});

@@ -119,19 +120,19 @@

eachStatement([
{op: "=", value: false, prop: "page"},
{op: "=", value: "published", prop: "status", func: "and"},
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'published', prop: 'status', func: 'and'},
{group: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
], func: 'and'}
], single);
single.callCount.should.eql(5);
single.getCall(0).calledWith({op: "=", value: false, prop: "page"}).should.eql(true);
single.getCall(1).calledWith({op: "=", value: "published", prop: "status", func: "and"}).should.eql(true);
single.getCall(2).calledWith({op: "!=", value: "joe", prop: "author"}).should.eql(true);
single.getCall(3).calledWith({op: "=", value: "photo", prop: "tag"}).should.eql(true);
single.getCall(4).calledWith({op: "=", value: "video", prop: "tag", func: "or"}).should.eql(true);
single.getCall(0).calledWith({op: '=', value: false, prop: 'page'}).should.eql(true);
single.getCall(1).calledWith({op: '=', value: 'published', prop: 'status', func: 'and'}).should.eql(true);
single.getCall(2).calledWith({op: '!=', value: 'joe', prop: 'author'}).should.eql(true);
single.getCall(3).calledWith({op: '=', value: 'photo', prop: 'tag'}).should.eql(true);
single.getCall(4).calledWith({op: '=', value: 'video', prop: 'tag', func: 'or'}).should.eql(true);
});

@@ -149,19 +150,18 @@

testFunc([
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
]);
single.callCount.should.eql(3);
single.getCall(0).calledWith({op: "!=", value: "joe", prop: "author"}).should.eql(true);
single.getCall(1).calledWith({op: "=", value: "photo", prop: "tag"}).should.eql(true);
single.getCall(2).calledWith({op: "=", value: "video", prop: "tag", func: "or"}).should.eql(true);
single.getCall(0).calledWith({op: '!=', value: 'joe', prop: 'author'}).should.eql(true);
single.getCall(1).calledWith({op: '=', value: 'photo', prop: 'tag'}).should.eql(true);
single.getCall(2).calledWith({op: '=', value: 'video', prop: 'tag', func: 'or'}).should.eql(true);
group.callCount.should.eql(1);
group.getCall(0).calledWith({group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}).should.eql(true);
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}).should.eql(true);
});

@@ -179,35 +179,35 @@

testFunc([
{op: "=", value: false, prop: "page"},
{op: "=", value: "published", prop: "status", func: "and"},
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'published', prop: 'status', func: 'and'},
{group: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
], func: 'and'}
]);
single.callCount.should.eql(5);
single.getCall(0).calledWith({op: "=", value: false, prop: "page"}).should.eql(true);
single.getCall(1).calledWith({op: "=", value: "published", prop: "status", func: "and"}).should.eql(true);
single.getCall(2).calledWith({op: "!=", value: "joe", prop: "author"}).should.eql(true);
single.getCall(3).calledWith({op: "=", value: "photo", prop: "tag"}).should.eql(true);
single.getCall(4).calledWith({op: "=", value: "video", prop: "tag", func: "or"}).should.eql(true);
single.getCall(0).calledWith({op: '=', value: false, prop: 'page'}).should.eql(true);
single.getCall(1).calledWith({op: '=', value: 'published', prop: 'status', func: 'and'}).should.eql(true);
single.getCall(2).calledWith({op: '!=', value: 'joe', prop: 'author'}).should.eql(true);
single.getCall(3).calledWith({op: '=', value: 'photo', prop: 'tag'}).should.eql(true);
single.getCall(4).calledWith({op: '=', value: 'video', prop: 'tag', func: 'or'}).should.eql(true);
group.callCount.should.eql(2);
group.getCall(0).calledWith({group: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'
}
], func: "and"}).should.eql(true);
], func: 'and'}).should.eql(true);
group.getCall(1).calledWith({group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}).should.eql(true);
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}).should.eql(true);
});

@@ -276,13 +276,12 @@ });

var statements = [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
];
findStatement(statements, {value: 'photo'}).should.eql(true);
findStatement(statements, {op: "=", value: "photo", prop: "tag"}, 'value').should.eql(true);
findStatement(statements, {op: "=", value: "photo", prop: "tag"}, ['value', 'prop']).should.eql(true);
findStatement(statements, {op: '=', value: 'photo', prop: 'tag'}, 'value').should.eql(true);
findStatement(statements, {op: '=', value: 'photo', prop: 'tag'}, ['value', 'prop']).should.eql(true);
findStatement(statements, {prop: /^tag/}).should.eql(true);

@@ -299,3 +298,3 @@ findStatement(statements, {prop: 'page'}).should.eql(false);

return lodashStmt.matchStatement(statement, fields ? _.pick(match, fields) : match);
}
};
};

@@ -325,14 +324,14 @@

var statements = [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
];
rejectStatements(statements, testFunction({value: 'video'})).should.eql([
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'}
], func: 'and'}
]);

@@ -343,11 +342,11 @@ });

var statements = [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'}
];
rejectStatements(statements, testFunction({prop: 'tag'})).should.eql([
{op: "!=", value: "joe", prop: "author"}
{op: '!=', value: 'joe', prop: 'author'}
]);

@@ -358,8 +357,8 @@ });

var statements = [
{op: "=", value: false, prop: "page"},
{op: "=", value: "cameron", prop: "author", func: "or"}
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
];
rejectStatements(statements, testFunction({prop: "page"})).should.eql([
{op: "=", value: "cameron", prop: "author"}
rejectStatements(statements, testFunction({prop: 'page'})).should.eql([
{op: '=', value: 'cameron', prop: 'author'}
]);

@@ -370,16 +369,16 @@ });

var statements = [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"},
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'},
{group: [
{op: "=", value: false, prop: "page"},
{op: "=", value: "cameron", prop: "author", func: "or"}
], func: "and"}
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
], func: 'and'}
];
rejectStatements(statements, testFunction({prop: "page"})).should.eql( [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"},
rejectStatements(statements, testFunction({prop: 'page'})).should.eql([
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'},
{group: [
{op: "=", value: "cameron", prop: "author"}
], func: "and"}
{op: '=', value: 'cameron', prop: 'author'}
], func: 'and'}
]);

@@ -391,15 +390,15 @@ });

{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
]},
{group: [
{op: "=", value: false, prop: "page"},
{op: "=", value: "cameron", prop: "author", func: "or"}
], func: "and"}
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
], func: 'and'}
];
rejectStatements(statements, testFunction({prop: "tag"})).should.eql([
rejectStatements(statements, testFunction({prop: 'tag'})).should.eql([
{group: [
{op: "=", value: false, prop: "page"},
{op: "=", value: "cameron", prop: "author", func: "or"}
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
]}

@@ -412,12 +411,12 @@ ]);

{group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
]},
{op: "=", value: false, prop: "page", func: "and"},
{op: "=", value: "cameron", prop: "author", func: "or"}
{op: '=', value: false, prop: 'page', func: 'and'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
];
rejectStatements(statements, testFunction({prop: "tag"})).should.eql([
{op: "=", value: false, prop: "page"},
{op: "=", value: "cameron", prop: "author", func: "or"}
rejectStatements(statements, testFunction({prop: 'tag'})).should.eql([
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'cameron', prop: 'author', func: 'or'}
]);

@@ -433,3 +432,3 @@ });

var result = mergeStatements();
result.should.eql({statements: []})
result.should.eql({statements: []});
});

@@ -439,3 +438,3 @@

var result = mergeStatements(undefined, undefined);
result.should.eql({statements: []})
result.should.eql({statements: []});
});

@@ -445,3 +444,3 @@

var result = mergeStatements(null, null);
result.should.eql({statements: []})
result.should.eql({statements: []});
});

@@ -452,10 +451,10 @@ });

var result = mergeStatements(
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published"}
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published'}
);
result.should.eql({statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
]})
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]});
});

@@ -466,8 +465,8 @@

null,
{prop: "status", op: "=", value: "published"}
{prop: 'status', op: '=', value: 'published'}
);
result.should.eql({statements: [
{prop: "status", op: "=", value: "published"}
]})
{prop: 'status', op: '=', value: 'published'}
]});
});

@@ -478,8 +477,8 @@

undefined,
{prop: "status", op: "=", value: "published"}
{prop: 'status', op: '=', value: 'published'}
);
result.should.eql({statements: [
{prop: "status", op: "=", value: "published"}
]})
{prop: 'status', op: '=', value: 'published'}
]});
});

@@ -489,8 +488,8 @@

var obj1 = {statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]},
obj2 = {statements: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
]},

@@ -500,7 +499,7 @@ result = mergeStatements(obj1, obj2);

result.should.eql({statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"},
{op: "=", value: "photo", prop: "tag", func: "and"},
{op: "=", value: "video", prop: "tag", func: "or"}
]})
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'},
{op: '=', value: 'photo', prop: 'tag', func: 'and'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
]});
});

@@ -510,4 +509,4 @@

var obj1 = {statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]},

@@ -518,5 +517,5 @@ obj2 = {statements: []},

result.should.eql({statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
]})
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]});
});

@@ -526,4 +525,4 @@

var obj1 = {statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]},

@@ -534,5 +533,5 @@ obj2 = null,

result.should.eql({statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
]})
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]});
});

@@ -542,12 +541,12 @@

var obj1 = {statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]},
obj2 = undefined,
obj2,
result = mergeStatements(obj1, obj2);
result.should.eql({statements: [
{prop: "page", op: "=", value: false},
{prop: "status", op: "=", value: "published", func: "and"}
]})
{prop: 'page', op: '=', value: false},
{prop: 'status', op: '=', value: 'published', func: 'and'}
]});
});

@@ -572,11 +571,11 @@ });

printStatements([
{op: "!=", value: "joe", prop: "author"},
{op: "=", value: "photo", prop: "tag", func: "and"},
{op: "=", value: "video", prop: "tag", func: "or"}
{op: '!=', value: 'joe', prop: 'author'},
{op: '=', value: 'photo', prop: 'tag', func: 'and'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
]);
consoleSpy.callCount.should.eql(3);
consoleSpy.getCall(0).args.should.eql(['', {op: "!=", value: "joe", prop: "author"}]);
consoleSpy.getCall(1).args.should.eql(['', {op: "=", value: "photo", prop: "tag", func: "and"}]);
consoleSpy.getCall(2).args.should.eql(['', {op: "=", value: "video", prop: "tag", func: "or"}]);
consoleSpy.getCall(0).args.should.eql(['', {op: '!=', value: 'joe', prop: 'author'}]);
consoleSpy.getCall(1).args.should.eql(['', {op: '=', value: 'photo', prop: 'tag', func: 'and'}]);
consoleSpy.getCall(2).args.should.eql(['', {op: '=', value: 'video', prop: 'tag', func: 'or'}]);
});

@@ -588,8 +587,8 @@

printStatements([
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'
}

@@ -599,6 +598,6 @@ ]);

consoleSpy.callCount.should.eql(4);
consoleSpy.getCall(0).args.should.eql(['', {op: "!=", value: "joe", prop: "author"}]);
consoleSpy.getCall(0).args.should.eql(['', {op: '!=', value: 'joe', prop: 'author'}]);
consoleSpy.getCall(1).args.should.eql(['', 'group', 'and']);
consoleSpy.getCall(2).args.should.eql([' ', {op: "=", value: "photo", prop: "tag"}]);
consoleSpy.getCall(3).args.should.eql([' ', {op: "=", value: "video", prop: "tag", func: "or"}]);
consoleSpy.getCall(2).args.should.eql([' ', {op: '=', value: 'photo', prop: 'tag'}]);
consoleSpy.getCall(3).args.should.eql([' ', {op: '=', value: 'video', prop: 'tag', func: 'or'}]);
});

@@ -610,14 +609,14 @@

printStatements([
{op: "=", value: false, prop: "page"},
{op: "=", value: "published", prop: "status", func: "and"},
{op: '=', value: false, prop: 'page'},
{op: '=', value: 'published', prop: 'status', func: 'and'},
{
group: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: "video", prop: "tag", func: "or"}
], func: "and"
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: 'video', prop: 'tag', func: 'or'}
], func: 'and'
}
], func: "and"
], func: 'and'
}

@@ -627,12 +626,11 @@ ]);

consoleSpy.callCount.should.eql(7);
consoleSpy.getCall(0).args.should.eql(['', {op: "=", value: false, prop: "page"}]);
consoleSpy.getCall(1).args.should.eql(['', {op: "=", value: "published", prop: "status", func: "and"}]);
consoleSpy.getCall(0).args.should.eql(['', {op: '=', value: false, prop: 'page'}]);
consoleSpy.getCall(1).args.should.eql(['', {op: '=', value: 'published', prop: 'status', func: 'and'}]);
consoleSpy.getCall(2).args.should.eql(['', 'group', 'and']);
consoleSpy.getCall(3).args.should.eql([' ', {op: "!=", value: "joe", prop: "author"}]);
consoleSpy.getCall(3).args.should.eql([' ', {op: '!=', value: 'joe', prop: 'author'}]);
consoleSpy.getCall(4).args.should.eql([' ', 'group', 'and']);
consoleSpy.getCall(5).args.should.eql([' ', {op: "=", value: "photo", prop: "tag"}]);
consoleSpy.getCall(6).args.should.eql([' ', {op: "=", value: "video", prop: "tag", func: "or"}]);
consoleSpy.getCall(5).args.should.eql([' ', {op: '=', value: 'photo', prop: 'tag'}]);
consoleSpy.getCall(6).args.should.eql([' ', {op: '=', value: 'video', prop: 'tag', func: 'or'}]);
});
});
});

@@ -0,1 +1,3 @@

/* globals describe, it */
/* jshint unused:false */
var should = require('should'),

@@ -5,21 +7,21 @@ gql = require('../lib/gql');

describe('Parser', function () {
var parserError = /^Parse error on line 1:/;
var parserError = /^Query Error: unexpected character in filter at char/;
describe('Operators', function () {
it('can parse standard equals', function () {
gql.parse("count:5").should.eql({
gql.parse('count:5').should.eql({
statements: [
{"prop": "count", "op": "=", "value": 5}
{prop: 'count', op: '=', value: 5}
]
});
gql.parse("tag:getting-started").should.eql({
gql.parse('tag:getting-started').should.eql({
statements: [
{"prop": "tag", "op": "=", "value": "getting-started"}
{prop: 'tag', op: '=', value: 'getting-started'}
]
});
gql.parse("author:'Joe Bloggs'").should.eql({
gql.parse('author:\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": "=", "value": "Joe Bloggs"}
{prop: 'author', op: '=', value: 'Joe Bloggs'}
]

@@ -30,17 +32,17 @@ });

it('can parse not equals', function () {
gql.parse("count:-5").should.eql({
gql.parse('count:-5').should.eql({
statements: [
{"prop": "count", "op": "!=", "value": 5}
{prop: 'count', op: '!=', value: 5}
]
});
gql.parse("tag:-getting-started").should.eql({
gql.parse('tag:-getting-started').should.eql({
statements: [
{"prop": "tag", "op": "!=", "value": "getting-started"}
{prop: 'tag', op: '!=', value: 'getting-started'}
]
});
gql.parse("author:-'Joe Bloggs'").should.eql({
gql.parse('author:-\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": "!=", "value": "Joe Bloggs"}
{prop: 'author', op: '!=', value: 'Joe Bloggs'}
]

@@ -51,17 +53,17 @@ });

it('can parse greater than', function () {
gql.parse("count:>5").should.eql({
gql.parse('count:>5').should.eql({
statements: [
{"prop": "count", "op": ">", "value": 5}
{prop: 'count', op: '>', value: 5}
]
});
gql.parse("tag:>getting-started").should.eql({
gql.parse('tag:>getting-started').should.eql({
statements: [
{"prop": "tag", "op": ">", "value": "getting-started"}
{prop: 'tag', op: '>', value: 'getting-started'}
]
});
gql.parse("author:>'Joe Bloggs'").should.eql({
gql.parse('author:>\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": ">", "value": "Joe Bloggs"}
{prop: 'author', op: '>', value: 'Joe Bloggs'}
]

@@ -72,17 +74,17 @@ });

it('can parse less than', function () {
gql.parse("count:<5").should.eql({
gql.parse('count:<5').should.eql({
statements: [
{"prop": "count", "op": "<", "value": 5}
{prop: 'count', op: '<', value: 5}
]
});
gql.parse("tag:<getting-started").should.eql({
gql.parse('tag:<getting-started').should.eql({
statements: [
{"prop": "tag", "op": "<", "value": "getting-started"}
{prop: 'tag', op: '<', value: 'getting-started'}
]
});
gql.parse("author:<'Joe Bloggs'").should.eql({
gql.parse('author:<\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": "<", "value": "Joe Bloggs"}
{prop: 'author', op: '<', value: 'Joe Bloggs'}
]

@@ -93,17 +95,17 @@ });

it('can parse greater than or equals', function () {
gql.parse("count:>=5").should.eql({
gql.parse('count:>=5').should.eql({
statements: [
{"prop": "count", "op": ">=", "value": 5}
{prop: 'count', op: '>=', value: 5}
]
});
gql.parse("tag:>=getting-started").should.eql({
gql.parse('tag:>=getting-started').should.eql({
statements: [
{"prop": "tag", "op": ">=", "value": "getting-started"}
{prop: 'tag', op: '>=', value: 'getting-started'}
]
});
gql.parse("author:>='Joe Bloggs'").should.eql({
gql.parse('author:>=\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": ">=", "value": "Joe Bloggs"}
{prop: 'author', op: '>=', value: 'Joe Bloggs'}
]

@@ -114,17 +116,17 @@ });

it('can parse less than or equals', function () {
gql.parse("count:<=5").should.eql({
gql.parse('count:<=5').should.eql({
statements: [
{"prop": "count", "op": "<=", "value": 5}
{prop: 'count', op: '<=', value: 5}
]
});
gql.parse("tag:<=getting-started").should.eql({
gql.parse('tag:<=getting-started').should.eql({
statements: [
{"prop": "tag", "op": "<=", "value": "getting-started"}
{prop: 'tag', op: '<=', value: 'getting-started'}
]
});
gql.parse("author:<='Joe Bloggs'").should.eql({
gql.parse('author:<=\'Joe Bloggs\'').should.eql({
statements: [
{"prop": "author", "op": "<=", "value": "Joe Bloggs"}
{prop: 'author', op: '<=', value: 'Joe Bloggs'}
]

@@ -135,17 +137,17 @@ });

it('can parse IN with single value', function () {
gql.parse("count:[5]").should.eql({
gql.parse('count:[5]').should.eql({
statements: [
{"prop": "count", "op": "IN", "value": [5]}
{prop: 'count', op: 'IN', value: [5]}
]
});
gql.parse("tag:[getting-started]").should.eql({
gql.parse('tag:[getting-started]').should.eql({
statements: [
{"prop": "tag", "op": "IN", "value": ["getting-started"]}
{prop: 'tag', op: 'IN', value: ['getting-started']}
]
});
gql.parse("author:['Joe Bloggs']").should.eql({
gql.parse('author:[\'Joe Bloggs\']').should.eql({
statements: [
{"prop": "author", "op": "IN", "value": ["Joe Bloggs"]}
{prop: 'author', op: 'IN', value: ['Joe Bloggs']}
]

@@ -156,17 +158,17 @@ });

it('can parse NOT IN with single value', function () {
gql.parse("count:-[5]").should.eql({
gql.parse('count:-[5]').should.eql({
statements: [
{"prop": "count", "op": "NOT IN", "value": [5]}
{prop: 'count', op: 'NOT IN', value: [5]}
]
});
gql.parse("tag:-[getting-started]").should.eql({
gql.parse('tag:-[getting-started]').should.eql({
statements: [
{"prop": "tag", "op": "NOT IN", "value": ["getting-started"]}
{prop: 'tag', op: 'NOT IN', value: ['getting-started']}
]
});
gql.parse("author:-['Joe Bloggs']").should.eql({
gql.parse('author:-[\'Joe Bloggs\']').should.eql({
statements: [
{"prop": "author", "op": "NOT IN", "value": ["Joe Bloggs"]}
{prop: 'author', op: 'NOT IN', value: ['Joe Bloggs']}
]

@@ -177,17 +179,17 @@ });

it('can parse IN with multiple values', function () {
gql.parse("count:[5, 8, 12]").should.eql({
gql.parse('count:[5, 8, 12]').should.eql({
statements: [
{"prop": "count", "op": "IN", "value": [5, 8, 12]}
{prop: 'count', op: 'IN', value: [5, 8, 12]}
]
});
gql.parse("tag:[getting-started, ghost, really-long-1]").should.eql({
gql.parse('tag:[getting-started, ghost, really-long-1]').should.eql({
statements: [
{"prop": "tag", "op": "IN", "value": ["getting-started", "ghost", "really-long-1"]}
{prop: 'tag', op: 'IN', value: ['getting-started', 'ghost', 'really-long-1']}
]
});
gql.parse("author:['Joe Bloggs', 'John O\\\'Nolan', 'Hello World']").should.eql({
gql.parse('author:[\'Joe Bloggs\', \'John O\\\'Nolan\', \'Hello World\']').should.eql({
statements: [
{"prop": "author", "op": "IN", "value": ["Joe Bloggs", "John O\'Nolan", "Hello World"]}
{prop: 'author', op: 'IN', value: ['Joe Bloggs', 'John O\'Nolan', 'Hello World']}
]

@@ -198,17 +200,17 @@ });

it('can parse NOT IN with single value', function () {
gql.parse("count:-[5, 8, 12]").should.eql({
gql.parse('count:-[5, 8, 12]').should.eql({
statements: [
{"prop": "count", "op": "NOT IN", "value": [5, 8, 12]}
{prop: 'count', op: 'NOT IN', value: [5, 8, 12]}
]
});
gql.parse("tag:-[getting-started, ghost, really-long-1]").should.eql({
gql.parse('tag:-[getting-started, ghost, really-long-1]').should.eql({
statements: [
{"prop": "tag", "op": "NOT IN", "value": ["getting-started", "ghost", "really-long-1"]}
{prop: 'tag', op: 'NOT IN', value: ['getting-started', 'ghost', 'really-long-1']}
]
});
gql.parse("author:-['Joe Bloggs', 'John O\\\'Nolan', 'Hello World']").should.eql({
gql.parse('author:-[\'Joe Bloggs\', \'John O\\\'Nolan\', \'Hello World\']').should.eql({
statements: [
{"prop": "author", "op": "NOT IN", "value": ["Joe Bloggs", "John O\'Nolan", "Hello World"]}
{prop: 'author', op: 'NOT IN', value: ['Joe Bloggs', 'John O\'Nolan', 'Hello World']}
]

@@ -223,5 +225,5 @@ });

statements: [
{"prop": "image", "op": "IS", "value": null}
{prop: 'image', op: 'IS', value: null}
]
})
});
});

@@ -232,5 +234,5 @@

statements: [
{"prop": "image", "op": "IS NOT", "value": null}
{prop: 'image', op: 'IS NOT', value: null}
]
})
});
});

@@ -241,5 +243,5 @@

statements: [
{"prop": "featured", "op": "=", "value": true}
{prop: 'featured', op: '=', value: true}
]
})
});
});

@@ -250,5 +252,5 @@

statements: [
{"prop": "featured", "op": "!=", "value": true}
{prop: 'featured', op: '!=', value: true}
]
})
});
});

@@ -259,5 +261,5 @@

statements: [
{"prop": "featured", "op": "=", "value": false}
{prop: 'featured', op: '=', value: false}
]
})
});
});

@@ -268,5 +270,5 @@

statements: [
{"prop": "featured", "op": "!=", "value": false}
{prop: 'featured', op: '!=', value: false}
]
})
});
});

@@ -277,5 +279,5 @@

statements: [
{"prop": "count", "op": "=", "value": 5}
{prop: 'count', op: '=', value: 5}
]
})
});
});

@@ -286,5 +288,5 @@

statements: [
{"prop": "count", "op": "!=", "value": 5}
{prop: 'count', op: '!=', value: 5}
]
})
});
});

@@ -295,14 +297,13 @@ });

it('should parse simple id & value combos', function () {
gql.parse("id:3").should.eql({
gql.parse('id:3').should.eql({
statements: [
{"prop": "id", "op": "=", "value": 3}
{prop: 'id', op: '=', value: 3}
]
});
gql.parse("slug:getting-started").should.eql({
gql.parse('slug:getting-started').should.eql({
statements: [
{"prop": "slug", "op": "=", "value": "getting-started"}
{prop: 'slug', op: '=', value: 'getting-started'}
]
});
});

@@ -313,15 +314,15 @@ });

it('many expressions', function () {
gql.parse("tag:photo+featured:true,tag.count:>5").should.eql({
gql.parse('tag:photo+featured:true,tag.count:>5').should.eql({
statements: [
{op: "=", value: "photo", prop: "tag"},
{op: "=", value: true, prop: "featured", func: "and"},
{op: ">", value: 5, prop: "tag.count", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: true, prop: 'featured', func: 'and'},
{op: '>', value: 5, prop: 'tag.count', func: 'or'}
]
});
gql.parse("tag:photo+image:-null,tag.count:>5").should.eql({
gql.parse('tag:photo+image:-null,tag.count:>5').should.eql({
statements: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "and"},
{op: ">", value: 5, prop: "tag.count", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'and'},
{op: '>', value: 5, prop: 'tag.count', func: 'or'}
]

@@ -332,11 +333,11 @@ });

it('grouped expressions', function () {
gql.parse("author:-joe+(tag:photo,image:-null,featured:true)").should.eql({
gql.parse('author:-joe+(tag:photo,image:-null,featured:true)').should.eql({
statements: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "or"},
{op: "=", value: true, prop: "featured", func: "or"}
], func: "and"
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
], func: 'and'
}

@@ -346,24 +347,24 @@ ]

gql.parse("(tag:photo,image:-null,featured:true)+author:-joe").should.eql({
gql.parse('(tag:photo,image:-null,featured:true)+author:-joe').should.eql({
statements: [
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "or"},
{op: "=", value: true, prop: "featured", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
]
},
{op: "!=", value: "joe", prop: "author", func: "and"}
{op: '!=', value: 'joe', prop: 'author', func: 'and'}
]
});
gql.parse("author:-joe,(tag:photo,image:-null,featured:true)").should.eql({
gql.parse('author:-joe,(tag:photo,image:-null,featured:true)').should.eql({
statements: [
{op: "!=", value: "joe", prop: "author"},
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "or"},
{op: "=", value: true, prop: "featured", func: "or"}
], func: "or"
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
], func: 'or'
}

@@ -373,12 +374,12 @@ ]

gql.parse("(tag:photo,image:-null,featured:false),author:-joe").should.eql({
gql.parse('(tag:photo,image:-null,featured:false),author:-joe').should.eql({
statements: [
{
group: [
{op: "=", value: "photo", prop: "tag"},
{op: "IS NOT", value: null, prop: "image", func: "or"},
{op: "=", value: false, prop: "featured", func: "or"}
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: false, prop: 'featured', func: 'or'}
]
},
{op: "!=", value: "joe", prop: "author", func: "or"}
{op: '!=', value: 'joe', prop: 'author', func: 'or'}
]

@@ -389,22 +390,22 @@ });

it('in expressions', function () {
gql.parse("author:-joe+tag:[photo,video]").should.eql({
gql.parse('author:-joe+tag:[photo,video]').should.eql({
statements: [
{op: "!=", value: "joe", prop: "author"},
{op: "IN", value: ["photo", "video"], prop: "tag", func: "and"}
{op: '!=', value: 'joe', prop: 'author'},
{op: 'IN', value: ['photo', 'video'], prop: 'tag', func: 'and'}
]
});
gql.parse("author:-joe+tag:-[photo,video,audio]").should.eql({
gql.parse('author:-joe+tag:-[photo,video,audio]').should.eql({
statements: [
{op: "!=", value: "joe", prop: "author"},
{op: "NOT IN", value: ["photo", "video", "audio"], prop: "tag", func: "and"}
{op: '!=', value: 'joe', prop: 'author'},
{op: 'NOT IN', value: ['photo', 'video', 'audio'], prop: 'tag', func: 'and'}
]
});
gql.parse("author:-joe+tag:[photo,video,magic,\'audio\']+post.count:>5+post.count:<100").should.eql({
gql.parse('author:-joe+tag:[photo,video,magic,\'audio\']+post.count:>5+post.count:<100').should.eql({
statements: [
{op: "!=", value: "joe", prop: "author"},
{op: "IN", value: ["photo", "video", "magic", "audio"], prop: "tag", func: "and"},
{op: ">", value: 5, prop: "post.count", func: "and"},
{op: "<", value: 100, prop: "post.count", func: "and"}
{op: '!=', value: 'joe', prop: 'author'},
{op: 'IN', value: ['photo', 'video', 'magic', 'audio'], prop: 'tag', func: 'and'},
{op: '>', value: 5, prop: 'post.count', func: 'and'},
{op: '<', value: 100, prop: 'post.count', func: 'and'}
]

@@ -417,8 +418,8 @@ });

it('will ignore whitespace in expressions', function () {
gql.parse("count: -5").should.eql(gql.parse("count:-5"));
gql.parse("author: -joe + tag: [photo, video]").should.eql(gql.parse("author:-joe+tag:[photo,video]"));
gql.parse('count: -5').should.eql(gql.parse('count:-5'));
gql.parse('author: -joe + tag: [photo, video]').should.eql(gql.parse('author:-joe+tag:[photo,video]'));
});
it('will not ignore whitespace in Strings', function () {
gql.parse("author:'Hello World'").should.not.eql(gql.parse("author:'HelloWorld'"));
gql.parse('author:\'Hello World\'').should.not.eql(gql.parse('author:\'HelloWorld\''));
});

@@ -429,27 +430,27 @@ });

it('CANNOT parse characters outside of a STRING value', function () {
(function () { gql.parse("tag:'My Tag'-") }).should.throw(parserError);
(function () { gql.parse('tag:\'My Tag\'-');}).should.throw(parserError);
});
it('CANNOT parse property - operator - value in wrong order', function () {
(function () { gql.parse("'My Tag':tag") }).should.throw(parserError);
(function () { gql.parse("5>:tag") }).should.throw(parserError);
(function () { gql.parse('\'My Tag\':tag');}).should.throw(parserError);
(function () { gql.parse('5>:tag');}).should.throw(parserError);
});
it('CANNOT parse combination without filter expression', function () {
(function () { gql.parse("count:3+") }).should.throw(parserError);
(function () { gql.parse(",count:3") }).should.throw(parserError);
(function () { gql.parse('count:3+');}).should.throw(parserError);
(function () { gql.parse(',count:3');}).should.throw(parserError);
});
it('CANNOT parse incomplete group', function () {
(function () { gql.parse("id:5,(count:3") }).should.throw(parserError);
(function () { gql.parse("count:3)") }).should.throw(parserError);
(function () { gql.parse("id:5(count:3)") }).should.throw(parserError);
(function () { gql.parse('id:5,(count:3');}).should.throw(parserError);
(function () { gql.parse('count:3)');}).should.throw(parserError);
(function () { gql.parse('id:5(count:3)');}).should.throw(parserError);
});
it('CANNOT parse invalid IN expression', function () {
(function () { gql.parse("id:[test+ing]") }).should.throw(parserError);
(function () { gql.parse("id:[test") }).should.throw(parserError);
(function () { gql.parse("id:test,ing]") }).should.throw(parserError);
})
(function () { gql.parse('id:[test+ing]');}).should.throw(parserError);
(function () { gql.parse('id:[test');}).should.throw(parserError);
(function () { gql.parse('id:test,ing]');}).should.throw(parserError);
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc