├── index.js ├── .gitignore ├── package.json ├── example └── app.js ├── LICENSE ├── README.md ├── test └── jsonrpc.test.js ├── lib └── jsonrpc-connector.js └── CONTRIBUTING.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/jsonrpc-connector'); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | .idea 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loopback-connector-jsonrpc", 3 | "version": "1.0.1", 4 | "description": "Loopback JSONRPC Connector", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js" 8 | }, 9 | "dependencies": { 10 | "jayson": "~1.0.11" 11 | }, 12 | "peerDependencies": { 13 | "loopback-datasource-juggler": "2.x.x" 14 | }, 15 | "devDependencies": { 16 | "loopback-datasource-juggler": "2.x.x", 17 | "loopback": "2.x.x", 18 | "should": "~1.3.0", 19 | "mocha": "~1.13.0" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/strongloop/loopback-connector-jsonrpc.git" 24 | }, 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | var jayson = require('jayson'); 2 | 3 | // create a server 4 | var server = jayson.server({ 5 | add: function (a, b, callback) { 6 | callback(null, a + b); 7 | }, 8 | subtract: function (a, b, callback) { 9 | callback(null, a - b); 10 | } 11 | }); 12 | 13 | var loopback = require("loopback"); 14 | 15 | var ds = loopback.createDataSource({ 16 | connector: require("../index"), 17 | debug: false, 18 | url: 'http://localhost:3000', 19 | operations: ['add', 'subtract']}); 20 | 21 | var model = ds.createModel('dummy'); 22 | 23 | var app = loopback(); 24 | 25 | app.use(loopback.rest()); 26 | app.use(server.middleware(server)); 27 | 28 | // Bind a http interface to the server and let it listen to localhost:3000 29 | var s = app.listen(3000, function () { 30 | 31 | model.add(1, 2, function(err, data) { 32 | console.log(err, data); 33 | s.close(); 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 StrongLoop, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LoopBack JSON-RPC Connector 2 | 3 | The LoopBack JSON-RPC Connector allows you to call [JSON-RPC](http://www.jsonrpc.org) services from LoopBack models. It 4 | uses [jayson](https://github.com/tedeh/jayson) as the client library. 5 | 6 | ## Usage 7 | 8 | 9 | ```js 10 | 11 | var ds = loopback.createDataSource({ 12 | connector: require("loopback-connector-jsonrpc"), 13 | debug: false, 14 | baseURL: 'http://localhost:3000', 15 | operations: ['add', 'subtract']}); 16 | 17 | var model = ds.createModel('dummy'); 18 | 19 | model.add(1, 2, function(err, data) { 20 | console.log(err, data); 21 | }); 22 | ``` 23 | 24 | Options to configure the connector: 25 | 26 | * url: Base URL to the json-rpc server 27 | * operations: An array of operation names 28 | 29 | You can also configure the baseURL as follows. 30 | 31 | { 32 | host: 'localhost', 33 | port: 3000 34 | } 35 | 36 | Other properties will be passed to `jayson`. 37 | 38 | * reviver: Function to use as a JSON reviver 39 | * replacer: Function to use as a JSON replacer 40 | * generator: Function to generate request ids with. If omitted, Jayson will just generate a "random" number that is RFC4122 41 | compliant and looks similar to this: 3d4be346-b5bb-4e28-bc4a-0b721d4f9ef9 42 | * version: Can be either 1 or 2 depending on which specification should be followed in communicating with the server. 43 | Defaults to 2 for JSON-RPC 2.0 44 | * encoding: String that determines the encoding to use and defaults to utf8 45 | 46 | 47 | 48 | ## License 49 | MIT 50 | -------------------------------------------------------------------------------- /test/jsonrpc.test.js: -------------------------------------------------------------------------------- 1 | var loopback = require("loopback"); 2 | var jayson = require('jayson'); 3 | var assert = require('assert'); 4 | 5 | describe('JSON-RPC connector', function () { 6 | var app, s, model; 7 | before(function (done) { 8 | // create a server 9 | var server = jayson.server({ 10 | add: function (a, b, callback) { 11 | callback(null, a + b); 12 | }, 13 | subtract: function (a, b, callback) { 14 | callback(null, a - b); 15 | }, 16 | divide: function (a, b, callback) { 17 | if (b === 0) { 18 | callback('Cannot divide by 0'); 19 | } else { 20 | callback(null, a / b); 21 | } 22 | } 23 | }); 24 | 25 | 26 | var ds = loopback.createDataSource({ 27 | connector: require("../index"), 28 | debug: false, 29 | url: 'http://localhost:3000', 30 | operations: ['add', 'subtract', 'multiply', 'divide']}); 31 | 32 | model = ds.createModel('dummy'); 33 | 34 | app = loopback(); 35 | 36 | app.use(loopback.bodyParser()); 37 | app.use(server.middleware(server)); 38 | s = app.listen(3000, done); 39 | }); 40 | 41 | it('invokes add json-rpc services', function (done) { 42 | 43 | model.add(1, 2, function (err, data) { 44 | assert.equal(data, 3); 45 | done(); 46 | }); 47 | 48 | }); 49 | 50 | it('invokes subtract json-rpc services', function (done) { 51 | 52 | model.subtract(1, 2, function (err, data) { 53 | assert.equal(data, -1); 54 | done(); 55 | }); 56 | 57 | }); 58 | 59 | it('reports unknown method', function (done) { 60 | 61 | model.multiply(1, 5, function (err, data) { 62 | assert.equal(err.code, -32601); 63 | assert.equal(err.message, 'Method not found'); 64 | done(); 65 | }); 66 | 67 | }); 68 | 69 | it('reports error response', function (done) { 70 | 71 | model.divide(1, 0, function (err, data) { 72 | assert(err.code, -32603); 73 | done(); 74 | }); 75 | 76 | }); 77 | 78 | after(function (done) { 79 | s.close(done); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /lib/jsonrpc-connector.js: -------------------------------------------------------------------------------- 1 | var jayson = require('jayson'); 2 | var url = require('url'); 3 | 4 | /** 5 | * Export the initialize method to loopback-datasource-juggler 6 | * @param dataSource 7 | * @param callback 8 | */ 9 | exports.initialize = function initializeDataSource(dataSource, callback) { 10 | var settings = dataSource.settings || {}; 11 | 12 | var connector = new JsonRpcConnector(settings); 13 | connector.getDataAccessObject(); 14 | 15 | dataSource.connector = connector; 16 | dataSource.connector.dataSource = dataSource; 17 | 18 | for (var f in connector.DataAccessObject) { 19 | dataSource[f] = connector.DataAccessObject[f]; 20 | } 21 | 22 | if (callback) { 23 | process.nextTick(callback); 24 | } 25 | 26 | } 27 | 28 | 29 | /** 30 | * The JsonRpcConnector constructor 31 | * @param options 32 | * @constructor 33 | */ 34 | function JsonRpcConnector(options) { 35 | if (options.url || options.baseURL) { 36 | var parts = url.parse(options.url || options.baseURL); 37 | parts['host'] = parts['host'].split(':')[0]; 38 | for (var p in parts) { 39 | if (!options.hasOwnProperty(p)) { 40 | options[p] = parts[p]; 41 | } 42 | } 43 | } 44 | if (options.debug) { 45 | console.log('Options: ', options); 46 | } 47 | this.options = options; 48 | 49 | if (options.operations) { 50 | this.client = jayson.client.http(options); 51 | } 52 | } 53 | 54 | JsonRpcConnector.prototype.mapOperation = function (op) { 55 | var client = this.client; 56 | var fn = function () { 57 | var args = Array.prototype.slice.call(arguments); 58 | var cb = null; 59 | if (args.length > 0 && typeof args[args.length - 1] === 'function') { 60 | cb = args.pop(); 61 | } 62 | client.request(op, args, function (err, res) { 63 | if(err) { 64 | cb && cb(err); 65 | return; 66 | } 67 | err = res && res.error; 68 | cb && cb(err, res && res.result); 69 | }); 70 | } 71 | return fn; 72 | } 73 | 74 | JsonRpcConnector.prototype.getDataAccessObject = function () { 75 | if (this.DataAccessObject) { 76 | return this.DataAccessObject; 77 | } 78 | var self = this; 79 | var DataAccessObject = function () { 80 | }; 81 | self.DataAccessObject = DataAccessObject; 82 | 83 | self.options.operations.forEach(function (op) { 84 | if (self.options.debug) { 85 | console.log('Mixing in method: ', op); 86 | } 87 | self.DataAccessObject[op] = self.mapOperation(op); 88 | }); 89 | return self.DataAccessObject; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing ### 2 | 3 | Thank you for your interest in `loopback-connector-jsonrpc`, an open source project 4 | administered by StrongLoop. 5 | 6 | Contributing to `loopback-connector-jsonrpc` is easy. In a few simple steps: 7 | 8 | * Ensure that your effort is aligned with the project's roadmap by 9 | talking to the maintainers, especially if you are going to spend a 10 | lot of time on it. 11 | 12 | * Make something better or fix a bug. 13 | 14 | * Adhere to code style outlined in the [Google C++ Style Guide][] and 15 | [Google Javascript Style Guide][]. 16 | 17 | * Sign the [Contributor License Agreement](https://cla.strongloop.com/strongloop/loopback-connector-jsonrpc) 18 | 19 | * Submit a pull request through Github. 20 | 21 | 22 | ### Contributor License Agreement ### 23 | 24 | ``` 25 | Individual Contributor License Agreement 26 | 27 | By signing this Individual Contributor License Agreement 28 | ("Agreement"), and making a Contribution (as defined below) to 29 | StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and 30 | agree to the following terms and conditions for Your present and 31 | future Contributions submitted to StrongLoop. Except for the license 32 | granted in this Agreement to StrongLoop and recipients of software 33 | distributed by StrongLoop, You reserve all right, title, and interest 34 | in and to Your Contributions. 35 | 36 | 1. Definitions 37 | 38 | "You" or "Your" shall mean the copyright owner or the individual 39 | authorized by the copyright owner that is entering into this 40 | Agreement with StrongLoop. 41 | 42 | "Contribution" shall mean any original work of authorship, 43 | including any modifications or additions to an existing work, that 44 | is intentionally submitted by You to StrongLoop for inclusion in, 45 | or documentation of, any of the products owned or managed by 46 | StrongLoop ("Work"). For purposes of this definition, "submitted" 47 | means any form of electronic, verbal, or written communication 48 | sent to StrongLoop or its representatives, including but not 49 | limited to communication or electronic mailing lists, source code 50 | control systems, and issue tracking systems that are managed by, 51 | or on behalf of, StrongLoop for the purpose of discussing and 52 | improving the Work, but excluding communication that is 53 | conspicuously marked or otherwise designated in writing by You as 54 | "Not a Contribution." 55 | 56 | 2. You Grant a Copyright License to StrongLoop 57 | 58 | Subject to the terms and conditions of this Agreement, You hereby 59 | grant to StrongLoop and recipients of software distributed by 60 | StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, 61 | royalty-free, irrevocable copyright license to reproduce, prepare 62 | derivative works of, publicly display, publicly perform, 63 | sublicense, and distribute Your Contributions and such derivative 64 | works under any license and without any restrictions. 65 | 66 | 3. You Grant a Patent License to StrongLoop 67 | 68 | Subject to the terms and conditions of this Agreement, You hereby 69 | grant to StrongLoop and to recipients of software distributed by 70 | StrongLoop a perpetual, worldwide, non-exclusive, no-charge, 71 | royalty-free, irrevocable (except as stated in this Section) 72 | patent license to make, have made, use, offer to sell, sell, 73 | import, and otherwise transfer the Work under any license and 74 | without any restrictions. The patent license You grant to 75 | StrongLoop under this Section applies only to those patent claims 76 | licensable by You that are necessarily infringed by Your 77 | Contributions(s) alone or by combination of Your Contributions(s) 78 | with the Work to which such Contribution(s) was submitted. If any 79 | entity institutes a patent litigation against You or any other 80 | entity (including a cross-claim or counterclaim in a lawsuit) 81 | alleging that Your Contribution, or the Work to which You have 82 | contributed, constitutes direct or contributory patent 83 | infringement, any patent licenses granted to that entity under 84 | this Agreement for that Contribution or Work shall terminate as 85 | of the date such litigation is filed. 86 | 87 | 4. You Have the Right to Grant Licenses to StrongLoop 88 | 89 | You represent that You are legally entitled to grant the licenses 90 | in this Agreement. 91 | 92 | If Your employer(s) has rights to intellectual property that You 93 | create, You represent that You have received permission to make 94 | the Contributions on behalf of that employer, that Your employer 95 | has waived such rights for Your Contributions, or that Your 96 | employer has executed a separate Corporate Contributor License 97 | Agreement with StrongLoop. 98 | 99 | 5. The Contributions Are Your Original Work 100 | 101 | You represent that each of Your Contributions are Your original 102 | works of authorship (see Section 8 (Submissions on Behalf of 103 | Others) for submission on behalf of others). You represent that to 104 | Your knowledge, no other person claims, or has the right to claim, 105 | any right in any intellectual property right related to Your 106 | Contributions. 107 | 108 | You also represent that You are not legally obligated, whether by 109 | entering into an agreement or otherwise, in any way that conflicts 110 | with the terms of this Agreement. 111 | 112 | You represent that Your Contribution submissions include complete 113 | details of any third-party license or other restriction (including, 114 | but not limited to, related patents and trademarks) of which You 115 | are personally aware and which are associated with any part of 116 | Your Contributions. 117 | 118 | 6. You Don't Have an Obligation to Provide Support for Your Contributions 119 | 120 | You are not expected to provide support for Your Contributions, 121 | except to the extent You desire to provide support. You may provide 122 | support for free, for a fee, or not at all. 123 | 124 | 6. No Warranties or Conditions 125 | 126 | StrongLoop acknowledges that unless required by applicable law or 127 | agreed to in writing, You provide Your Contributions on an "AS IS" 128 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 129 | EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES 130 | OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR 131 | FITNESS FOR A PARTICULAR PURPOSE. 132 | 133 | 7. Submission on Behalf of Others 134 | 135 | If You wish to submit work that is not Your original creation, You 136 | may submit it to StrongLoop separately from any Contribution, 137 | identifying the complete details of its source and of any license 138 | or other restriction (including, but not limited to, related 139 | patents, trademarks, and license agreements) of which You are 140 | personally aware, and conspicuously marking the work as 141 | "Submitted on Behalf of a Third-Party: [named here]". 142 | 143 | 8. Agree to Notify of Change of Circumstances 144 | 145 | You agree to notify StrongLoop of any facts or circumstances of 146 | which You become aware that would make these representations 147 | inaccurate in any respect. Email us at callback@strongloop.com. 148 | ``` 149 | 150 | [Google C++ Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml 151 | [Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml 152 | --------------------------------------------------------------------------------