From 943b417077833c95be428015ed5661cb76fd1d8d Mon Sep 17 00:00:00 2001 From: terrycojones Date: Sat, 5 May 2012 13:05:01 -0400 Subject: [PATCH 1/3] Fixed some variables (var missing, redeclarations) and some uses of == and != instead of === and !==. --- fluidinfo.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/fluidinfo.js b/fluidinfo.js index 01ba6b7..4b659c0 100644 --- a/fluidinfo.js +++ b/fluidinfo.js @@ -131,10 +131,10 @@ var fluidinfo = function(options) { } } } - if (options.access_token != undefined){ + if (options.access_token !== undefined){ OAuthAccessToken = options.access_token; } - if ((options.username != undefined) && (options.password != undefined)) { + if ((options.username !== undefined) && (options.password !== undefined)) { authorizationBase64Fragment = Base64.encode( options.username + ":" + options.password); // Makes sure the logged in user's username is available via the @@ -168,7 +168,7 @@ var fluidinfo = function(options) { * @return {string} The appropriately encoded URL. */ function encodeURL(path) { - var result = ""; + var i, result = ""; for (i = 0; i < path.length; i++) { result += "/" + encodeURIComponent(path[i]); } @@ -201,7 +201,6 @@ var fluidinfo = function(options) { // check for an array (potential set) and validate it only contains // strings (currently multi-type arrays are not allowed) if (isArray(value)) { - var i; for (i = 0; i < value.length; i++) { var memberType = typeof(value[i]); if (memberType !== "string") { @@ -229,8 +228,8 @@ var fluidinfo = function(options) { // then check if the value is a Fluidinfo primitive type. Otherwise // complain. var result = null; - if (options.type==="PUT" && (options.path.match("^objects\/") || - options.path.match("^about\/"))) { + if (options.type === "PUT" && (options.path.match("^objects\/") || + options.path.match("^about\/"))) { if (options.contentType){ result = options.contentType; } else if (isPrimitive(options.data)) { @@ -464,7 +463,7 @@ var fluidinfo = function(options) { } else { xhr.onreadystatechange = function() { // Old skool handling of XHR for older browsers. - if (xhr.readyState != 4) return; + if (xhr.readyState !== 4) return; handleResult(); }; } @@ -573,7 +572,7 @@ var fluidinfo = function(options) { var objectID; for (objectID in data.id){ if (typeof data.id[objectID] !== "function") { - var obj = new Object(); + var tag, obj = new Object(); obj["id"] = objectID; for (tag in data.id[objectID]) { if (typeof data.id[objectID][tag] !== "function") { @@ -633,7 +632,7 @@ var fluidinfo = function(options) { var queries = []; var updateSpecification = []; updateSpecification[0] = options.where; - var valueSpec = new Object(); + var val, valueSpec = new Object(); for (val in options.values){ if (typeof options.values[val] !== "function") { if(options.values[val] === undefined) { @@ -780,8 +779,8 @@ var fluidinfo = function(options) { */ session.recent = function(opts) { var options = clone(opts); - if (options == undefined) { - var options = {}; + if (options === undefined) { + options = {}; } if (options.about) { From c3c43b0d663e952cbb282bbe619f3a073517fb63 Mon Sep 17 00:00:00 2001 From: terrycojones Date: Sat, 5 May 2012 15:17:59 -0400 Subject: [PATCH 2/3] Fixed missing 'var' in tests, removed one stray trailing comma. --- spec/fluidinfoSpec.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/fluidinfoSpec.js b/spec/fluidinfoSpec.js index 62213fb..3bbe2f6 100644 --- a/spec/fluidinfoSpec.js +++ b/spec/fluidinfoSpec.js @@ -512,6 +512,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.api.get(options); var expected = ["path", "onSuccess", "onError"]; + var attribute; for(attribute in options) { if(typeof(options[attribute]) !== 'function') { var isExpected = false; @@ -589,6 +590,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.api.post(options); var expected = ["path", "data", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -615,7 +617,7 @@ describe("Fluidinfo.js", function() { data: "data", onSuccess: function(result) { expect(result.status).toEqual(204); - }, + } }); var responseStatus = 204; var responseHeaders = {"Content-Type": "text/html", @@ -656,6 +658,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.api.put(options); var expected = ["path", "data", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -714,6 +717,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.api.del(options); var expected = ["path", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -780,6 +784,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.api.head(options); var expected = ["path", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1012,6 +1017,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.query(options); var expected = ["select", "where", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1266,6 +1272,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.update(options); var expected = ["values", "where", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1315,7 +1322,7 @@ describe("Fluidinfo.js", function() { where: where, onSuccess: function(result) {}, onError: function(result) {}}); - expected = "https://fluiddb.fluidinfo.com/values"; + var expected = "https://fluiddb.fluidinfo.com/values"; expect(this.server.requests[0].url).toEqual(expected); expect(this.server.requests[0].method).toEqual("PUT"); expect(this.server.requests[0].requestHeaders["content-type"]) @@ -1402,6 +1409,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.tag(options); var expected = ["values", "about", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1564,6 +1572,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.del(options); var expected = ["tags", "where", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1704,6 +1713,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.getObject(options); var expected = ["select", "about", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -1993,6 +2003,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.createObject(options); var expected = ["about", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; @@ -2147,6 +2158,7 @@ describe("Fluidinfo.js", function() { }; // must not mutate this.fi.recent(options); var expected = ["where", "onSuccess", "onError"]; + var attribute; for(attribute in options) { var isExpected = false; var i; From daa6c6a1faab6e143c3586430818412744e4a286 Mon Sep 17 00:00:00 2001 From: terrycojones Date: Sat, 5 May 2012 16:49:33 -0400 Subject: [PATCH 3/3] Added JSON RPC support. --- fluidinfo.js | 49 ++++++++++++++ spec/fluidinfoSpec.js | 149 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) diff --git a/fluidinfo.js b/fluidinfo.js index 4b659c0..d1b74aa 100644 --- a/fluidinfo.js +++ b/fluidinfo.js @@ -537,6 +537,55 @@ var fluidinfo = function(options) { return sendRequest(options); }; + /** + * Make a JSON RPC call (via HTTP POST) to the Fluidinfo API. + * See http://www.simple-is-better.org/json-rpc/jsonrpc20.html for + * the JSON RPC specification. + */ + api.jsonrpc = function(opts){ + /* + * param opts: a JS object containing attributes: + * method: the (string) name of the remote method. + * onError: (optional) the error callback function. + * onSuccess: (optional) the success callback function. + * params: the parameters to pass to the remote method, + * either a JS object or array. + */ + var options = clone(opts); + var originalOnSuccess = options.onSuccess; + var originalOnError = options.onError; + + options.onSuccess = function(result){ + if (result.data.hasOwnProperty('result')){ + // Success. + if (originalOnSuccess){ + result.data = result.data.result; + originalOnSuccess(result); + } + } + else { + // Error. + if (originalOnError){ + // We could overwrite result.status and result.statusText + // here, but I think it's better to just pass the error + // object (which contains a 'code' and 'message' and + // possibly a 'data'. + result.data = result.data.error; + originalOnError(result); + } + } + }; + options.type = 'POST'; + options.path = 'jsonrpc'; + options.data = { + id: 1, // Just send a constant request id for now. + jsonrpc: '2.0', + method: options.method, + params: options.params + }; + return sendRequest(options); + }; + session.api = api; /** diff --git a/spec/fluidinfoSpec.js b/spec/fluidinfoSpec.js index 3bbe2f6..d40cd48 100644 --- a/spec/fluidinfoSpec.js +++ b/spec/fluidinfoSpec.js @@ -801,6 +801,155 @@ describe("Fluidinfo.js", function() { }); }); }); + + describe("JSONRPC", function() { + describe("default behaviour", function() { + beforeEach(function() { + var spy = sinon.spy(); + this.fi.api.jsonrpc({ + method: "add-comment", + onSuccess: function(result) { + spy(); + expect(result.status).toEqual(200); + expect(result.data) + .toEqual({ + "fluidinfo.com/info/about" : [ 'nothing' ], + "fluidinfo.com/info/text" : 'a comment about nothing', + "fluidinfo.com/info/timestamp" : 1336086338.758058, + "fluidinfo.com/info/url" : 'http://comments.com', + "fluidinfo.com/info/username" : 'username' + }); + }, + params: { + about: "nothing", + text: "a comment about nothing" + } + }); + var responseStatus = 200; + var responseHeaders = {"Content-Type": "application/json"}; + var responseText = ( + '{' + + '"id": 1, ' + + '"jsonrpc": "2.0",' + + '"result": {' + + '"fluidinfo.com/info/about": ["nothing"],' + + '"fluidinfo.com/info/text": "a comment about nothing",' + + '"fluidinfo.com/info/timestamp": 1336086338.7580581,' + + '"fluidinfo.com/info/url": "http://comments.com",' + + '"fluidinfo.com/info/username": "username"' + + '}' + + '}' + ); + this.server.requests[0].respond( + responseStatus, responseHeaders, responseText); + expect(spy.calledOnce).toBeTruthy(); + }); + + it_should_be_a_standard_ajax_request(); + + it_should_be_authenticated(); + + it("should be a POST method", function() { + expect(this.server.requests[0].method) + .toEqual("POST"); + }); + + it("should have a payload", function() { + expect(this.server.requests[0].requestBody) + .not.toEqual(null); + expect(this.server.requests[0].requestBody) + .toBeTruthy(); + }); + + it_should_have_a_content_type_of("application/json"); + + it("should not mutate the user-provided option object", + function() { + var method = 'a-remote-method'; + var params = {name: "name", description: "a description"}; + var onSuccess = function(result) {}; + var onError = function(result) {}; + var options = { + method: method, + onSuccess: onSuccess, + onError: onError, + params: params + }; // must not mutate + this.fi.api.jsonrpc(options); + var expected = ["method", "params", "onSuccess", "onError"]; + var attribute; + for(attribute in options) { + var isExpected = false; + var i; + for(i = 0; i < expected.length; i++) { + if(expected[i] === attribute) { + isExpected = true; + } + } + expect(isExpected).toEqual(true); + } + expect(options.method).toEqual(method); + expect(options.onSuccess).toEqual(onSuccess); + expect(options.onError).toEqual(onError); + expect(options.params).toEqual(params); + }); + }); + + describe("error behaviour", function() { + beforeEach(function() { + var spy = sinon.spy(); + this.fi.api.jsonrpc({ + method: "add-comment", + onError: function(result) { + spy(); + expect(result.status).toEqual(200); + expect(result.data) + .toEqual({ + code: 400, + message: 'Sorry...' + }); + }, + params: { + about: "nothing", + text: "a comment about nothing" + } + }); + var responseStatus = 200; + var responseHeaders = {"Content-Type": "application/json"}; + var responseText = ( + '{' + + '"id": 1, ' + + '"jsonrpc": "2.0",' + + '"error": {' + + '"code": 400,' + + '"message": "Sorry..."' + + '}' + + '}' + ); + this.server.requests[0].respond( + responseStatus, responseHeaders, responseText); + expect(spy.calledOnce).toBeTruthy(); + }); + + it_should_be_a_standard_ajax_request(); + + it_should_be_authenticated(); + + it("should be a POST method", function() { + expect(this.server.requests[0].method) + .toEqual("POST"); + }); + + it("should have a payload", function() { + expect(this.server.requests[0].requestBody) + .not.toEqual(null); + expect(this.server.requests[0].requestBody) + .toBeTruthy(); + }); + + it_should_have_a_content_type_of("application/json"); + }); + }); }); /**