diff --git a/index.js b/index.js index 78e960a..8e93ad6 100755 --- a/index.js +++ b/index.js @@ -5,36 +5,37 @@ //===================================================== function parceFind(_levelA) { - -//+++++++++++++++++++++++++++++++++++ work over Array -//++++++++++++++++++++++++++++++++++++++++++++++++++++ - - let propsA = _levelA.map(function(currentValue, index) { - - let itemX = _levelA[index]; - - if( itemX instanceof Query){ - return itemX.toString(); - } else if ( ! Array.isArray(itemX) && "object" === typeof itemX ) { - let propsA = Object.keys(itemX); - if ( 1 !== propsA.length) { - throw new RangeError("Alias objects should only have one value. was passed: "+JSON.stringify(itemX)); - } - let propS = propsA[0]; - let item = itemX[propS]; - // contributor: https://github.com/charlierudolph/graphql-query-builder/commit/878328e857e92d140f5ba6f7cfe07837620ec490 - if (Array.isArray(item)) { - return new Query(propS).find(item) - } - return `${propS} : ${item} `; - } else if ( "string" === typeof itemX ) { - return itemX; - } else { - throw new RangeError("cannot handle Find value of "+itemX); - } - }); - - return propsA.join(","); + //+++++++++++++++++++++++++++++++++++ work over Array + //++++++++++++++++++++++++++++++++++++++++++++++++++++ + + let propsA = _levelA.map(function(currentValue, index) { + let itemX = _levelA[index]; + + if (itemX instanceof Query) { + return itemX.toString(); + } else if (!Array.isArray(itemX) && "object" === typeof itemX) { + let propsA = Object.keys(itemX); + if (1 !== propsA.length) { + throw new RangeError( + "Alias objects should only have one value. was passed: " + + JSON.stringify(itemX) + ); + } + let propS = propsA[0]; + let item = itemX[propS]; + // contributor: https://github.com/charlierudolph/graphql-query-builder/commit/878328e857e92d140f5ba6f7cfe07837620ec490 + if (Array.isArray(item)) { + return new Query(propS).find(item); + } + return `${propS} : ${item} `; + } else if ("string" === typeof itemX) { + return itemX; + } else { + throw new RangeError("cannot handle Find value of " + itemX); + } + }); + + return propsA.join(","); } //===================================================== @@ -42,91 +43,100 @@ function parceFind(_levelA) { //===================================================== function getGraphQLValue(value) { - if ("string" === typeof value) { - value = JSON.stringify(value); - } else if (Array.isArray(value)) { - value = value.map(item => { - return getGraphQLValue(item); - }).join(); - value = `[${value}]`; - } else if ("object" === typeof value) { - /*if (value.toSource) + if (value === null) { + return value; + } else if ("string" === typeof value) { + value = JSON.stringify(value); + } else if (Array.isArray(value)) { + value = value + .map(item => { + return getGraphQLValue(item); + }) + .join(); + value = `[${value}]`; + } else if ("object" === typeof value) { + /*if (value.toSource) value = value.toSource().slice(2,-2); else*/ - value = objectToString(value); - //console.error("No toSource!!",value); - } - return value; + value = objectToString(value); + if (value === "{}") return undefined; + //console.error("No toSource!!",value); + } + return value; } function objectToString(obj) { - let sourceA = []; - - for(let prop in obj){ + + for (let prop in obj) { if ("function" === typeof obj[prop]) { continue; } - // if ("object" === typeof obj[prop]) { - sourceA.push(`${prop}:${getGraphQLValue(obj[prop])}`); - // } else { - // sourceA.push(`${prop}:${obj[prop]}`); - // } + // if ("object" === typeof obj[prop]) { + let value = getGraphQLValue(obj[prop]); + if (typeof value !== "undefined") { + sourceA.push(`${prop}:${value}`); + } + // } else { + // sourceA.push(`${prop}:${obj[prop]}`); + // } } return `{${sourceA.join()}}`; } - - - //===================================================== //========================================= Query Class //===================================================== -function Query(_fnNameS, _aliasS_OR_Filter){ - - this.fnNameS = _fnNameS; - this.headA = []; - - this.filter = (filtersO) => { - - for(let propS in filtersO){ - if ("function" === typeof filtersO[propS]) { - continue; - } - let val = getGraphQLValue(filtersO[propS]); - if ("{}" === val) { - continue; - } - this.headA.push( `${propS}:${val}` ); - } - return this; - }; - - if ("string" === typeof _aliasS_OR_Filter) { - this.aliasS = _aliasS_OR_Filter; - } else if ("object" === typeof _aliasS_OR_Filter) { - this.filter(_aliasS_OR_Filter); - } else if (undefined === _aliasS_OR_Filter && 2 === arguments.length){ - throw new TypeError("You have passed undefined as Second argument to 'Query'"); - } else if (undefined !== _aliasS_OR_Filter){ - throw new TypeError("Second argument to 'Query' should be an alias name(String) or filter arguments(Object). was passed "+_aliasS_OR_Filter); +function Query(_fnNameS, _aliasS_OR_Filter) { + this.fnNameS = _fnNameS; + this.headA = []; + + this.filter = filtersO => { + for (let propS in filtersO) { + if ("function" === typeof filtersO[propS]) { + continue; + } + let val = getGraphQLValue(filtersO[propS]); + if (typeof val !== "undefined") { + this.headA.push(`${propS}:${val}`); + } } + return this; + }; - this.setAlias = (_aliasS) =>{ - this.aliasS = _aliasS; - return this; - }; - - this.find = function(findA) { // THIS NEED TO BE A "FUNCTION" to scope 'arguments' - if( ! findA){ - throw new TypeError("find value can not be >>falsy<<"); - } - // if its a string.. it may have other values - // else it sould be an Object or Array of maped values - this.bodyS = parceFind((Array.isArray(findA)) ? findA : Array.from(arguments)); - return this; - }; + if ("string" === typeof _aliasS_OR_Filter) { + this.aliasS = _aliasS_OR_Filter; + } else if ("object" === typeof _aliasS_OR_Filter) { + this.filter(_aliasS_OR_Filter); + } else if (undefined === _aliasS_OR_Filter && 2 === arguments.length) { + throw new TypeError( + "You have passed undefined as Second argument to 'Query'" + ); + } else if (undefined !== _aliasS_OR_Filter) { + throw new TypeError( + "Second argument to 'Query' should be an alias name(String) or filter arguments(Object). was passed " + + _aliasS_OR_Filter + ); + } + + this.setAlias = _aliasS => { + this.aliasS = _aliasS; + return this; + }; + + this.find = function(findA) { + // THIS NEED TO BE A "FUNCTION" to scope 'arguments' + if (!findA) { + throw new TypeError("find value can not be >>falsy<<"); + } + // if its a string.. it may have other values + // else it sould be an Object or Array of maped values + this.bodyS = parceFind( + Array.isArray(findA) ? findA : Array.from(arguments) + ); + return this; + }; } //===================================================== @@ -134,14 +144,17 @@ function Query(_fnNameS, _aliasS_OR_Filter){ //===================================================== Query.prototype = { - - toString : function(){ - if (undefined === this.bodyS) { - throw new ReferenceError("return properties are not defined. use the 'find' function to defined them"); - } - - return `${ (this.aliasS) ? (this.aliasS + ":") : "" } ${this.fnNameS } ${ (0 < this.headA.length)?"("+this.headA.join(",")+")":"" } { ${ this.bodyS } }`; + toString: function() { + if (undefined === this.bodyS) { + throw new ReferenceError( + "return properties are not defined. use the 'find' function to defined them" + ); } + + return `${this.aliasS ? this.aliasS + ":" : ""} ${this.fnNameS} ${ + 0 < this.headA.length ? "(" + this.headA.join(",") + ")" : "" + } { ${this.bodyS} }`; + } }; module.exports = Query; diff --git a/test.js b/test.js index 42e3bda..f7f9b75 100644 --- a/test.js +++ b/test.js @@ -1,200 +1,215 @@ "use strict"; -var expect = require('chai').expect; -var Query = require('./index'); +var expect = require("chai").expect; +var Query = require("./index"); function removeSpaces(textS) { - return `${textS}`.replace(/\s+/g, ''); + return `${textS}`.replace(/\s+/g, ""); } -describe("graphql query builder", function() { //log the function - - it('should accept a single find value', function(){ - let expeted = `user{age}`; - let user = new Query("user").find("age"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should create a Query with function name & alia', function(){ - - let expeted = `sam : user{name}`; - let user = new Query("user","sam").find("name"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should create a Query with function name & input', function(){ - - let expeted = `user(id:12345){name}`; - let user = new Query("user",{id : 12345}).find("name"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should create a Query with function name & input(s)', function(){ - - let expeted = `user(id:12345, age:34){name}`; - let user = new Query("user",{id : 12345, age:34}).find("name"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should accept a single find value with alia', function(){ - let expeted = `user{nickname:name}`; - let user = new Query("user").find({nickname:"name"}); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should accept a multiple find values', function(){ - let expeted = `user{firstname, lastname}`; - let user = new Query("user").find("firstname","lastname") - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should accept an array find values', function(){ - let expeted = `user{firstname, lastname}`; - let user = new Query("user").find(["firstname","lastname"]); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should work with nesting Querys', function(){ - - let expeted = `user( id:12345 ) { +describe("graphql query builder", function() { + //log the function + + it("should accept a single find value", function() { + let expeted = `user{age}`; + let user = new Query("user").find("age"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should create a Query with function name & alia", function() { + let expeted = `sam : user{name}`; + let user = new Query("user", "sam").find("name"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should create a Query with function name & input", function() { + let expeted = `user(id:12345){name}`; + let user = new Query("user", { id: 12345 }).find("name"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should create a Query with function name & input(s)", function() { + let expeted = `user(id:12345, age:34){name}`; + let user = new Query("user", { id: 12345, age: 34 }).find("name"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should accept a single find value with alia", function() { + let expeted = `user{nickname:name}`; + let user = new Query("user").find({ nickname: "name" }); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should accept a multiple find values", function() { + let expeted = `user{firstname, lastname}`; + let user = new Query("user").find("firstname", "lastname"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should accept an array find values", function() { + let expeted = `user{firstname, lastname}`; + let user = new Query("user").find(["firstname", "lastname"]); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); + + it("should work with nesting Querys", function() { + let expeted = `user( id:12345 ) { id, nickname : name, isViewerFriend, image : profilePicture( size:50 ) { uri, width, height } }`; - - let profilePicture = new Query("profilePicture",{size : 50}); - profilePicture.find( "uri", "width", "height"); - - let user = new Query("user",{id : 12345}); - user.find(["id", {"nickname":"name"}, "isViewerFriend", {"image":profilePicture}]); - expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); - }); - - it('should work with simple nesting Querys', function(){ + let profilePicture = new Query("profilePicture", { size: 50 }); + profilePicture.find("uri", "width", "height"); + + let user = new Query("user", { id: 12345 }); + user.find([ + "id", + { nickname: "name" }, + "isViewerFriend", + { image: profilePicture } + ]); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); + }); - let expeted = `user { profilePicture { uri, width, height } }` + it("should work with simple nesting Querys", function() { + let expeted = `user { profilePicture { uri, width, height } }`; let user = new Query("user"); - user.find({"profilePicture": ['uri', 'width', 'height']}); + user.find({ profilePicture: ["uri", "width", "height"] }); expect(removeSpaces(expeted)).to.equal(removeSpaces(user)); }); - - it('should be able to group Querys', function(){ - - let expeted = `FetchLeeAndSam { lee: user(id: "1") { name }, + + it("should be able to group Querys", function() { + let expeted = `FetchLeeAndSam { lee: user(id: "1") { name }, sam: user(id: "2") { name } }`; - - let FetchLeeAndSam = new Query("FetchLeeAndSam"); - - let lee = new Query("user",{id : '1'}); - lee.setAlias('lee'); - lee.find(["name"]); - - let sam = new Query("user","sam"); - sam.filter({id : '2'}); - sam.find("name"); - - FetchLeeAndSam.find(lee,sam); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(FetchLeeAndSam)); - }); - - it('should work with nasted objects and lists', function(){ - - let expeted =`myPost:Message(type:"chat",message:"yoyo", + + let FetchLeeAndSam = new Query("FetchLeeAndSam"); + + let lee = new Query("user", { id: "1" }); + lee.setAlias("lee"); + lee.find(["name"]); + + let sam = new Query("user", "sam"); + sam.filter({ id: "2" }); + sam.find("name"); + + FetchLeeAndSam.find(lee, sam); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(FetchLeeAndSam)); + }); + + it("should work with nasted objects and lists", function() { + let expeted = `myPost:Message(type:"chat",message:"yoyo", user:{name:"bob",screen:{height:1080,width:1920}}, friends:[{id:1,name:"ann"},{id:2,name:"tom"}]) { messageId : id, postedTime : createTime }`; - - let MessageRequest = { type:"chat", - message:"yoyo", - user:{ name:"bob", - screen:{ height:1080, width:1920 } }, - friends:[ {id:1,name:"ann"}, {id:2,name:"tom"} ] - }; - - let MessageQuery = new Query("Message","myPost"); - MessageQuery.filter(MessageRequest); - MessageQuery.find({ messageId : "id"}, {postedTime : "createTime" }); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(MessageQuery)); - - }); - - it('should work with objects that have help functions(will skip function name)', function(){ - - let expeted ='inventory(toy:"jack in the box") { id }'; - - let ChildsToy = { toy:"jack in the box", getState:function(){ } }; - - ChildsToy.getState();//for istanbul(coverage) to say all fn was called - - let ItemQuery = new Query("inventory",ChildsToy); - ItemQuery.find("id"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); - - }); - - it('should work with nasted objects that have help functions(will skip function name)', function(){ - - let expeted ='inventory(toy:"jack in the box") { id }'; - - let ChildsToy = { toy:"jack in the box", utils: { getState:function(){ } } }; - - ChildsToy.utils.getState();//for istanbul(coverage) to say all fn was called - - let ItemQuery = new Query("inventory",ChildsToy); - ItemQuery.find("id"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); - - }); - - it('should skip empty objects in filter/args', function(){ - - let expeted ='inventory(toy:"jack in the box") { id }'; - - let ChildsToy = { toy:"jack in the box", utils: { } }; - - let ItemQuery = new Query("inventory",ChildsToy); - ItemQuery.find("id"); - - expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); - }); - - it('should throw Error if find input items have zero props', function(){ - expect(() => new Query("x").find({})).to.throw(Error); - }); - - it('should throw Error if find input items have multiple props', function(){ - expect(() => new Query("x").find({a:"z",b:"y"})).to.throw(Error); - }); - - it('should throw Error if find is undefined', function(){ - expect(() => new Query("x").find()).to.throw(Error); - }); - - it('should throw Error if no find values have been set', function(){ - expect(() => `${new Query("x")}`).to.throw(Error); - }); - - it('should throw Error if find is not valid', function(){ - expect(() => new Query("x").find(123)).to.throw(Error); - }); - - it('should throw Error if you accidentally pass an undefined', function(){ - expect(() => new Query("x",undefined)).to.throw(Error); - }); - - it('should throw Error it is not an input object for alias', function(){ - expect(() => new Query("x",true)).to.throw(Error); - }); -}); \ No newline at end of file + + let MessageRequest = { + type: "chat", + message: "yoyo", + user: { + name: "bob", + screen: { height: 1080, width: 1920 } + }, + friends: [{ id: 1, name: "ann" }, { id: 2, name: "tom" }] + }; + + let MessageQuery = new Query("Message", "myPost"); + MessageQuery.filter(MessageRequest); + MessageQuery.find({ messageId: "id" }, { postedTime: "createTime" }); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(MessageQuery)); + }); + + it("should work with objects that have help functions(will skip function name)", function() { + let expeted = 'inventory(toy:"jack in the box") { id }'; + + let ChildsToy = { toy: "jack in the box", getState: function() {} }; + + ChildsToy.getState(); //for istanbul(coverage) to say all fn was called + + let ItemQuery = new Query("inventory", ChildsToy); + ItemQuery.find("id"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); + }); + + it("should work with nasted objects that have help functions(will skip function name)", function() { + let expeted = 'inventory(toy:"jack in the box") { id }'; + + let ChildsToy = { + toy: "jack in the box", + utils: { getState: function() {} } + }; + + ChildsToy.utils.getState(); //for istanbul(coverage) to say all fn was called + + let ItemQuery = new Query("inventory", ChildsToy); + ItemQuery.find("id"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); + }); + + it("should skip empty objects in filter/args", function() { + let expeted = 'inventory(toy:"jack in the box") { id }'; + + let ChildsToy = { toy: "jack in the box", utils: {} }; + + let ItemQuery = new Query("inventory", ChildsToy); + ItemQuery.find("id"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); + }); + + it("should skip empty objects in filter/args for nested objects", function() { + let expeted = + 'inventory(toy:"jack in the box",input:{blah:"foo",nullTest:null}) { id }'; + + let ChildsToy = { + toy: "jack in the box", + utils: {}, + input: { blah: "foo", nullTest: null, objectTest: {} } + }; + + let ItemQuery = new Query("inventory", ChildsToy); + ItemQuery.find("id"); + + expect(removeSpaces(expeted)).to.equal(removeSpaces(ItemQuery)); + }); + + it("should throw Error if find input items have zero props", function() { + expect(() => new Query("x").find({})).to.throw(Error); + }); + + it("should throw Error if find input items have multiple props", function() { + expect(() => new Query("x").find({ a: "z", b: "y" })).to.throw(Error); + }); + + it("should throw Error if find is undefined", function() { + expect(() => new Query("x").find()).to.throw(Error); + }); + + it("should throw Error if no find values have been set", function() { + expect(() => `${new Query("x")}`).to.throw(Error); + }); + + it("should throw Error if find is not valid", function() { + expect(() => new Query("x").find(123)).to.throw(Error); + }); + + it("should throw Error if you accidentally pass an undefined", function() { + expect(() => new Query("x", undefined)).to.throw(Error); + }); + + it("should throw Error it is not an input object for alias", function() { + expect(() => new Query("x", true)).to.throw(Error); + }); +});