├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ ├── Feature_request.md │ ├── Question.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── .npmrc ├── .nycrc ├── .travis.yml ├── CHANGES.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── docs.json ├── example ├── before-after.js ├── documentation │ ├── index.js │ └── remotes │ │ ├── contract-class.js │ │ ├── contract.js │ │ ├── index.js │ │ ├── simple-class.js │ │ └── simple.js ├── remote-fs.js ├── root.js ├── shared-class.js ├── simple-types.js ├── simple.js ├── socket-io │ ├── client.js │ ├── server.js │ └── test.txt └── streams.js ├── ext └── meta.js ├── index.js ├── intl ├── cs │ └── messages.json ├── de │ └── messages.json ├── en │ └── messages.json ├── es │ └── messages.json ├── fr │ └── messages.json ├── it │ └── messages.json ├── ja │ └── messages.json ├── ko │ └── messages.json ├── nl │ └── messages.json ├── pl │ └── messages.json ├── pt │ └── messages.json ├── ru │ └── messages.json ├── tr │ └── messages.json ├── zh-Hans │ └── messages.json └── zh-Hant │ └── messages.json ├── lib ├── context-base.js ├── exports-helper.js ├── http-context.js ├── http-invocation.js ├── jsonrpc-adapter.js ├── looks-like-json.js ├── number-checks.js ├── remote-objects.js ├── rest-adapter.js ├── shared-class.js ├── shared-method.js ├── socket-io-adapter.js ├── socket-io-context.js ├── type-registry.js └── types │ ├── any.js │ ├── array.js │ ├── boolean.js │ ├── date.js │ ├── geopoint.js │ ├── integer.js │ ├── number.js │ ├── object.js │ └── string.js ├── package.json └── test ├── auth.test.js ├── authorize-hook.test.js ├── data └── foo.json ├── e2e ├── e2e-server.js ├── fixtures │ ├── remotes.js │ └── user.js └── smoke.test.js ├── helpers ├── expect.js ├── shared-objects-factory.js └── test-server.js ├── http-invocation.test.js ├── jsonrpc.test.js ├── karma.conf.js ├── mocha.opts ├── phase-handlers.test.js ├── remote-objects.test.js ├── rest-adapter.test.js ├── rest-coercion.test.js ├── rest-coercion ├── README.md ├── _custom-class.context.js ├── _jsonbody.context.js ├── _jsonform.context.js ├── _urlencoded.context.js ├── jsonbody-any.suite.js ├── jsonbody-array.suite.js ├── jsonbody-geopoint.suite.js ├── jsonbody-object-type.suite.js ├── jsonbody-object.suite.js ├── jsonform-any.suite.js ├── jsonform-array.suite.js ├── jsonform-boolean.suite.js ├── jsonform-date.suite.js ├── jsonform-geopoint.suite.js ├── jsonform-integer.suite.js ├── jsonform-number.suite.js ├── jsonform-object-type.suite.js ├── jsonform-object.suite.js ├── jsonform-string.suite.js ├── urlencoded-any.suite.js ├── urlencoded-array.suite.js ├── urlencoded-boolean.suite.js ├── urlencoded-date.suite.js ├── urlencoded-geopoint.suite.js ├── urlencoded-integer.suite.js ├── urlencoded-number.suite.js ├── urlencoded-object-type.suite.js ├── urlencoded-object.suite.js └── urlencoded-string.suite.js ├── rest.browser.test.js ├── rest.test.js ├── shared-class.test.js ├── shared-method.test.js ├── streams.js └── type-registry.test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "loopback", 3 | "rules": { 4 | "max-len": ["error", 90, 4, { 5 | "ignoreComments": true, 6 | "ignoreUrls": true, 7 | "ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)" 8 | }] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | 6 | --- 7 | 8 | 21 | 22 | ## Steps to reproduce 23 | 24 | 25 | 26 | ## Current Behavior 27 | 28 | 29 | 30 | ## Expected Behavior 31 | 32 | 33 | 34 | ## Link to reproduction sandbox 35 | 36 | 40 | 41 | ## Additional information 42 | 43 | 48 | 49 | ## Related Issues 50 | 51 | 52 | 53 | _See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_ 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: feature 5 | 6 | --- 7 | 8 | 16 | 17 | ## Suggestion 18 | 19 | 20 | 21 | ## Use Cases 22 | 23 | 27 | 28 | ## Examples 29 | 30 | 31 | 32 | ## Acceptance criteria 33 | 34 | TBD - will be filled by the team. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help. 4 | labels: question 5 | 6 | --- 7 | 8 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Report a security vulnerability 4 | url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues 5 | about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead. 6 | - name: Get help on StackOverflow 7 | url: https://stackoverflow.com/tags/loopbackjs 8 | about: Please ask and answer questions on StackOverflow. 9 | - name: Join our mailing list 10 | url: https://groups.google.com/forum/#!forum/loopbackjs 11 | about: You can also post your question to our mailing list. 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ## Checklist 12 | 13 | 👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/strong-remoting) 👈 14 | 15 | - [ ] `npm test` passes on your machine 16 | - [ ] New tests added or existing tests modified to cover all changes 17 | - [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html) 18 | - [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html) 19 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - critical 10 | - p1 11 | - major 12 | # Label to use when marking an issue as stale 13 | staleLabel: stale 14 | # Comment to post when marking an issue as stale. Set to `false` to disable 15 | markComment: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | This issue has been closed due to continued inactivity. Thank you for your understanding. 22 | If you believe this to be in error, please contact one of the code owners, 23 | listed in the [`CODEOWNERS`](https://github.com/strongloop/loopback/blob/master/CODEOWNERS) file at the top-level of this repository. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.swp 10 | *.swo 11 | node_modules/ 12 | /coverage/ 13 | dist 14 | .nyc_output/ 15 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [ 3 | "Gruntfile.js", 4 | "test/**/*.js" 5 | ], 6 | "cache": true 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "8" 5 | - "10" 6 | - "12" 7 | 8 | after_success: npm run coverage 9 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners, 3 | # the last matching pattern has the most precendence. 4 | 5 | # Current maintainers 6 | 7 | * @bajtos @ritch @superkhau @fabien @clarkorz @ebarault @zbarbuto 8 | 9 | # Alumni 10 | 11 | _ @lehni 12 | -------------------------------------------------------------------------------- /docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | {"title": "Remote Objects API", "depth": 2}, 4 | "lib/remote-objects.js", 5 | "lib/shared-class.js", 6 | "lib/shared-method.js", 7 | "lib/http-context.js", 8 | "lib/http-invocation.js" 9 | ], 10 | "codeSectionDepth": 3 11 | } 12 | -------------------------------------------------------------------------------- /example/before-after.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const g = require('strong-globalize')(); 9 | // create a set of shared classes 10 | const remotes = require('../').create(); 11 | 12 | // expose a simple object 13 | const user = remotes.exports.user = { 14 | greet: function(fn) { 15 | fn(null, 'hello, world!'); 16 | }, 17 | }; 18 | 19 | // share the greet method 20 | user.greet.shared = true; 21 | 22 | // define a vanilla JavaScript class 23 | function Dog(name) { 24 | this.name = name; 25 | } 26 | 27 | // add a shared constructor 28 | Dog.sharedCtor = function(name, fn) { 29 | fn(null, new Dog(name)); 30 | }; 31 | 32 | // define the args for the shared constructor 33 | Dog.sharedCtor.accepts = {arg: 'name', type: 'string'}; 34 | 35 | // change the default routing 36 | Dog.sharedCtor.http = {path: '/:name'}; 37 | 38 | // define a regular instance method 39 | Dog.prototype.speak = function(fn) { 40 | fn(null, 'roof! my name is ' + this.name); 41 | }; 42 | 43 | Dog.prototype.speak.shared = true; 44 | 45 | // expose the dog class 46 | remotes.exports.dog = Dog; 47 | 48 | // do something before greet 49 | remotes.before('user.greet', function(ctx, next) { 50 | if ((ctx.req.param('password') || '').toString() !== '1234') { 51 | next(new Error(g.f('bad password!'))); 52 | } else { 53 | next(); 54 | } 55 | }); 56 | 57 | // do something before any user method 58 | remotes.before('user.*', function(ctx, next) { 59 | g.log('calling a user method'); 60 | next(); 61 | }); 62 | 63 | // do something before a dog instance method 64 | remotes.before('dog.prototype.*', function(ctx, next) { 65 | const dog = this; 66 | g.log('calling a method on %s', dog.name); 67 | next(); 68 | }); 69 | 70 | // do something after the dog speak method 71 | // note: you cannot cancel a method after 72 | // it has been called 73 | remotes.after('dog.prototype.speak', function(ctx, next) { 74 | g.log('after {{speak}}!'); 75 | next(); 76 | }); 77 | 78 | // do something before all methods 79 | remotes.before('**', function(ctx, next, method) { 80 | g.log('calling %s', method.name); 81 | next(); 82 | }); 83 | 84 | // modify all results 85 | remotes.after('**', function(ctx, next) { 86 | ctx.result += '!!!'; 87 | next(); 88 | }); 89 | 90 | // expose it over http 91 | require('http') 92 | .createServer(remotes.handler('rest')) 93 | .listen(3000); 94 | 95 | /* 96 | 97 | Test the above with curl or a rest client: 98 | 99 | $ node before-after.js 100 | $ curl http://localhost:3000/user/greet 101 | $ curl http://localhost:3000/user/greet?password=1234 102 | $ curl http://localhost:3000/dog/fido/speak 103 | 104 | */ 105 | -------------------------------------------------------------------------------- /example/documentation/index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const g = require('strong-globalize')(); 9 | const http = require('http'); 10 | const remotes = require('./remotes'); 11 | const meta = require('../../ext/meta'); 12 | const swagger = require('../../ext/swagger'); 13 | const port = process.argv[2] || 3000; 14 | let handler, adapter; 15 | 16 | // The installation order sets which routes are captured by Swagger. 17 | swagger(remotes, { 18 | basePath: 'http://localhost:3000', 19 | }); 20 | meta(remotes); 21 | 22 | http 23 | .createServer(remotes.handler('rest')) 24 | .listen(port, function(err) { 25 | if (err) { 26 | g.error('Failed to start server with: %s', err.stack || err.message || err); 27 | process.exit(1); 28 | } 29 | 30 | g.log('Listening on port %s...', port); 31 | }); 32 | -------------------------------------------------------------------------------- /example/documentation/remotes/contract-class.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // This example shows using the helper for a type in a "definitive" fashion. 9 | const helper = require('../../../').extend(module.exports); 10 | 11 | /** 12 | * A simple class that contains a name, this time with a custom HTTP contract. 13 | */ 14 | const clshelper = helper.type(ContractClass, { 15 | accepts: [{name: 'name', type: 'string', required: true}], 16 | http: {path: '/:name'}, 17 | }); 18 | function ContractClass(name) { 19 | this.name = name; 20 | } 21 | 22 | /** 23 | * Returns the ContractClass instance's name. 24 | */ 25 | clshelper.method(getName, { 26 | returns: {name: 'name', type: 'string'}, 27 | }); 28 | function getName(callback) { 29 | callback(null, this.name); 30 | } 31 | 32 | /** 33 | * Takes in a name, returning a greeting for that name. 34 | */ 35 | clshelper.method(greet, { 36 | accepts: [{name: 'other', type: 'string', required: true}], 37 | returns: {name: 'greeting', type: 'string'}, 38 | }); 39 | function greet(other, callback) { 40 | callback(null, 'Hi, ' + other + '!'); 41 | } 42 | 43 | /** 44 | * Returns the ContractClass prototype's favorite person's name. 45 | */ 46 | helper.method(getFavoritePerson, { 47 | path: 'ContractClass.getFavoritePerson', 48 | returns: {name: 'name', type: 'string'}, 49 | }); 50 | function getFavoritePerson(callback) { 51 | callback(null, 'You'); 52 | } 53 | -------------------------------------------------------------------------------- /example/documentation/remotes/contract.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const helper = require('../../../').extend(module.exports); 9 | 10 | /** 11 | * Returns a secret message. 12 | */ 13 | helper.method(getSecret, { 14 | http: {verb: 'GET', path: '/customizedGetSecret'}, 15 | returns: {name: 'secret', type: 'string'}, 16 | }); 17 | function getSecret(callback) { 18 | callback(null, 'shhh!'); 19 | } 20 | 21 | /** 22 | * Takes a string and returns an updated string. 23 | */ 24 | helper.method(transform, { 25 | http: {verb: 'PUT', path: '/customizedTransform'}, 26 | accepts: [{name: 'str', type: 'string', required: true}], 27 | returns: {name: 'str', type: 'string'}, 28 | }); 29 | function transform(str, callback) { 30 | callback(null, 'transformed: ' + str); 31 | } 32 | -------------------------------------------------------------------------------- /example/documentation/remotes/index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const remotes = require('../../../').create(); 9 | 10 | /** 11 | * Example API 12 | */ 13 | remotes.exports.simple = require('./simple'); 14 | remotes.exports.contract = require('./contract'); 15 | remotes.exports.SimpleClass = require('./simple-class').SimpleClass; 16 | remotes.exports.ContractClass = require('./contract-class').ContractClass; 17 | 18 | module.exports = remotes; 19 | -------------------------------------------------------------------------------- /example/documentation/remotes/simple-class.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // This example shows using the helper for a type in a "post-definition" style. 9 | const helper = require('../../../').extend(module.exports); 10 | 11 | /** 12 | * A simple class that contains a name. 13 | */ 14 | function SimpleClass(name) { 15 | this.name = name; 16 | } 17 | helper.type(SimpleClass, { 18 | description: 'A simple class example', 19 | accepts: [{name: 'name', type: 'string', required: true}], 20 | }); 21 | 22 | /** 23 | * Returns the SimpleClass instance's name. 24 | */ 25 | SimpleClass.prototype.getName = function(callback) { 26 | callback(null, this.name); 27 | }; 28 | helper.method(SimpleClass.prototype.getName, { 29 | path: 'SimpleClass.prototype.getName', 30 | description: 'Returns the SimpleClass instance\'s name.', 31 | returns: {name: 'name', type: 'string'}, 32 | }); 33 | 34 | /** 35 | * Takes in a name, returning a greeting for that name. 36 | */ 37 | SimpleClass.prototype.greet = function(other, callback) { 38 | callback(null, 'Hi, ' + other + '!'); 39 | }; 40 | helper.method(SimpleClass.prototype.greet, { 41 | path: 'SimpleClass.prototype.greet', 42 | description: 'Takes in a name, returning a greeting for that name.', 43 | accepts: [{name: 'other', type: 'string', required: true}], 44 | returns: {name: 'greeting', type: 'string'}, 45 | }); 46 | 47 | /** 48 | * Returns the SimpleClass prototype's favorite person's name. 49 | */ 50 | SimpleClass.getFavoritePerson = function(callback) { 51 | callback(null, 'You'); 52 | }; 53 | helper.method(SimpleClass.getFavoritePerson, { 54 | path: 'SimpleClass.getFavoritePerson', 55 | description: 'Returns the SimpleClass prototype\'s favorite person\'s name.', 56 | returns: {name: 'name', type: 'string'}, 57 | }); 58 | -------------------------------------------------------------------------------- /example/documentation/remotes/simple.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // This helper adds methods to a module that we assume will be added to the remotes. 9 | // TODO(schoon) - Make this _the_ API, not a "helper". 10 | // TODO(schoon) - Document EVERYTHING 11 | const helper = require('../../../').extend(module.exports); 12 | 13 | /** 14 | * Returns a secret message. 15 | */ 16 | helper.method(getSecret, { 17 | returns: {name: 'secret', type: 'string'}, 18 | }); 19 | function getSecret(callback) { 20 | callback(null, 'shhh!'); 21 | } 22 | 23 | /** 24 | * Takes a string and returns an updated string. 25 | */ 26 | helper.method(transform, { 27 | accepts: [{name: 'str', 28 | type: 'string', 29 | required: true, 30 | description: 'The value to update'}], 31 | returns: {name: 'str', type: 'string'}, 32 | description: 'Takes a string and returns an updated string.', 33 | }); 34 | function transform(str, callback) { 35 | callback(null, 'transformed: ' + str); 36 | } 37 | -------------------------------------------------------------------------------- /example/remote-fs.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // create a set of shared classes 9 | const remotes = require('../').create(); 10 | 11 | // share some fs module code 12 | const fs = remotes.exports.fs = require('fs'); 13 | 14 | // specifically the createReadStream function 15 | fs.createReadStream.shared = true; 16 | 17 | // describe the arguments 18 | fs.createReadStream.accepts = {arg: 'path', type: 'string'}; 19 | 20 | // describe the stream destination 21 | fs.createReadStream.http = { 22 | // pipe to the response 23 | // for the http transport 24 | pipe: { 25 | dest: 'res', 26 | }, 27 | }; 28 | 29 | // over rest / http 30 | require('http') 31 | .createServer(remotes.handler('rest')) 32 | .listen(3000); 33 | 34 | /* 35 | 36 | Test the above with curl or a rest client: 37 | 38 | $ node remote-fs.js 39 | $ curl http://localhost:3000/fs/createReadStream?path=simple.js 40 | 41 | */ 42 | -------------------------------------------------------------------------------- /example/root.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // create a set of shared classes 9 | const remotes = require('../').create(); 10 | 11 | // expose a simple object 12 | const products = remotes.exports.products = { 13 | find: function(fn) { 14 | fn(null, ['tv', 'vcr', 'radio']); 15 | }, 16 | }; 17 | 18 | // share the find method 19 | products.find.shared = true; 20 | products.find.returns = {arg: 'products', root: true, type: 'array'}; 21 | 22 | // expose it over http 23 | require('http') 24 | .createServer(remotes.handler('rest')) 25 | .listen(3000); 26 | 27 | /* 28 | 29 | Test the above with curl or a rest client: 30 | 31 | $ node root.js 32 | $ curl http://localhost:3000/products/find 33 | # responds as an array (instead of an object) 34 | [ 35 | "tv", 36 | "vcr", 37 | "radio" 38 | ] 39 | 40 | */ 41 | -------------------------------------------------------------------------------- /example/shared-class.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const express = require('express'); 9 | 10 | // define a vanilla JavaScript class 11 | function Dog(name) { 12 | this.name = name; 13 | } 14 | 15 | // add a shared constructor 16 | Dog.sharedCtor = function(name, fn) { 17 | fn(null, new Dog(name)); 18 | }; 19 | 20 | // define the args for the shared constructor 21 | Dog.sharedCtor.accepts = {arg: 'name', type: 'string', http: {source: 'path'}}; 22 | 23 | // change the default routing 24 | Dog.sharedCtor.http = {path: '/:name'}; 25 | 26 | // define a regular instance method 27 | Dog.prototype.speak = function(fn) { 28 | fn(null, 'roof! my name is ' + this.name); 29 | }; 30 | 31 | // mark it as shared 32 | Dog.prototype.speak.returns = {arg: 'result', type: 'string', root: true}; 33 | Dog.prototype.speak.shared = true; 34 | 35 | // create a set of shared classes 36 | const remotes = require('../').create(); 37 | 38 | // expose the Dog class 39 | remotes.exports.dog = Dog; 40 | 41 | const app = express(); 42 | app.use(remotes.handler('rest')); 43 | 44 | app.listen(3000); 45 | 46 | /* 47 | 48 | Test the above with curl or a rest client: 49 | 50 | $ node shared-class.js 51 | $ curl http://localhost:3000/dog/fido/speak 52 | roof! my name is fido 53 | 54 | */ 55 | -------------------------------------------------------------------------------- /example/simple-types.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // create a set of shared classes 9 | const remotes = require('../').create(); 10 | 11 | // expose a simple object 12 | const user = remotes.exports.user = { 13 | greet: function(fn) { 14 | fn(null, {msg: 'hello, world!'}); 15 | }, 16 | }; 17 | 18 | // share the greet method 19 | user.greet.shared = true; 20 | 21 | // expose it over http 22 | require('http') 23 | .createServer(remotes.handler('rest')) 24 | .listen(3000); 25 | 26 | /* 27 | 28 | Test the above with curl or a rest client: 29 | 30 | $ node simple.js 31 | $ curl http://localhost:3000/user/greet 32 | { 33 | "msg": "hello, world!" 34 | } 35 | 36 | */ 37 | -------------------------------------------------------------------------------- /example/simple.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // create a set of shared classes 9 | const remoting = require('../'); 10 | const SharedClass = remoting.SharedClass; 11 | const remotes = remoting.create(); 12 | const express = require('express'); 13 | const app = express(); 14 | 15 | // define a class-like object (or constructor) 16 | const user = { 17 | greet: function(fn) { 18 | fn(null, 'hello, world!'); 19 | }, 20 | }; 21 | 22 | // create a shared class to allow strong-remoting to map 23 | // http requests to method invocations on your class 24 | const userSharedClass = new SharedClass('user', user); 25 | 26 | // tell strong-remoting about your greet method 27 | userSharedClass.defineMethod('greet', { 28 | isStatic: true, // not an instance method 29 | returns: [{ 30 | arg: 'msg', 31 | type: 'string', // define the type of the callback arguments 32 | }], 33 | }); 34 | 35 | // tell strong-remoting about the class 36 | remotes.addClass(userSharedClass); 37 | 38 | // mount the middleware on an express app 39 | app.use(remotes.handler('rest')); 40 | 41 | // create the http server 42 | require('http') 43 | .createServer(app) 44 | .listen(3000); 45 | 46 | /* 47 | 48 | Test the above with curl or a rest client: 49 | 50 | $ node simple.js 51 | $ curl http://localhost:3000/user/greet 52 | # responds as an object, with the msg attribute 53 | # set to the result of the function 54 | { 55 | "msg": "hello, world!" 56 | } 57 | 58 | */ 59 | -------------------------------------------------------------------------------- /example/socket-io/client.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const g = require('strong-globalize')(); 9 | 10 | const Remotes = require('../../client/js/client'); 11 | const SocketIOAdapter = require('../../client/js/socket-io-adapter'); 12 | const remotes = Remotes.connect('http://localhost:3000', SocketIOAdapter); 13 | 14 | remotes.invoke('fs.readFile', {path: 'test.txt'}, function(err, data) { 15 | g.log(data.toString()); 16 | }); 17 | 18 | remotes.invoke('ee.on', {event: 'foo'}, function(err, data) { 19 | g.log('foo event ran! %s', data); // logged multiple times 20 | }); 21 | -------------------------------------------------------------------------------- /example/socket-io/server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // create a set of shared classes 9 | const remotes = require('../../').create(); 10 | 11 | // share some fs module code 12 | const fs = remotes.exports.fs = require('fs'); 13 | 14 | // specifically the readFile function 15 | fs.readFile.shared = true; 16 | 17 | // describe the arguments 18 | fs.readFile.accepts = {arg: 'path', type: 'string'}; 19 | 20 | // describe the result 21 | fs.readFile.returns = {arg: 'data', type: 'buffer'}; 22 | 23 | // event emitter 24 | const EventEmitter = require('events').EventEmitter; 25 | const ee = remotes.exports.ee = new EventEmitter(); 26 | 27 | // expose the on method 28 | ee.on.shared = true; 29 | ee.on.accepts = {arg: 'event', type: 'string'}; 30 | ee.on.returns = {arg: 'data', type: 'object'}; 31 | 32 | setInterval(function() { 33 | // emit some data 34 | ee.emit('foo', {some: 'data'}); 35 | }, 1000); 36 | 37 | // expose it over http 38 | const server = 39 | require('http') 40 | .createServer() 41 | .listen(3000); 42 | 43 | remotes.handler('socket-io', server); 44 | -------------------------------------------------------------------------------- /example/socket-io/test.txt: -------------------------------------------------------------------------------- 1 | hello, world! -------------------------------------------------------------------------------- /example/streams.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // faux remote stream 9 | const destination = process.stdout; 10 | const fs = require('fs'); 11 | const path = require('path'); 12 | 13 | // create a set of shared classes 14 | const remotes = require('../').create(); 15 | 16 | // our modules 17 | const fileService = remotes.exports.files = { 18 | upload: function() { 19 | return destination; 20 | }, 21 | download: function() { 22 | return fs.createReadStream(path.join(__dirname, 'streams.js')); 23 | }, 24 | }; 25 | 26 | fileService.upload.http = { 27 | // pipe to the request 28 | // to the result of the function 29 | pipe: { 30 | source: 'req', 31 | }, 32 | }; 33 | fileService.upload.shared = true; 34 | 35 | fileService.download.http = { 36 | // pipe to the response 37 | // for the http transport 38 | pipe: { 39 | dest: 'res', 40 | }, 41 | }; 42 | fileService.download.shared = true; 43 | 44 | // over rest / http 45 | require('http') 46 | .createServer(remotes.handler('rest')) 47 | .listen(3000); 48 | -------------------------------------------------------------------------------- /ext/meta.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | /** 9 | * Expose the `Meta` plugin. 10 | */ 11 | module.exports = Meta; 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | const Remoting = require('../'); 17 | 18 | /** 19 | * Create a remotable Meta module for plugging into `RemoteObjects`. 20 | */ 21 | function Meta(remotes, options) { 22 | // Unfold options. 23 | const name = (options && options.name) || 'meta'; 24 | 25 | // We need a temporary REST adapter to discover our available routes. 26 | const adapter = remotes.handler('rest').adapter; 27 | const extension = {}; 28 | const helper = Remoting.extend(extension); 29 | 30 | helper.method(routes, {returns: {type: 'object', root: true}}); 31 | function routes(callback) { 32 | callback(null, adapter.allRoutes()); 33 | } 34 | 35 | helper.method(classes, {returns: {type: 'object', root: true}}); 36 | function classes(callback) { 37 | callback(null, remotes.classes()); 38 | } 39 | 40 | remotes.exports[name] = extension; 41 | return extension; 42 | } 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | /** 9 | * remotes ~ public api 10 | */ 11 | 12 | const SG = require('strong-globalize'); 13 | SG.SetRootDir(__dirname); 14 | 15 | module.exports = require('./lib/remote-objects'); 16 | module.exports.SharedClass = require('./lib/shared-class'); 17 | -------------------------------------------------------------------------------- /intl/cs/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "chybné heslo!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Hodnota není číslo.", 4 | "224c1872889edc69277dfa73bb3486a4": "Neplatná návratová hodnota pro argument '{0}' typu '{1}': {2}. Přijatý typ byl {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Neexistuje metoda pro zpracování {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: vyřazen jiný než kořenový návratový argument {1} typu \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} je povinný argument", 8 | "2874eedf72302267612549956af600de": "nepodporovaný zdroj propojení procesů", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) již není podporováno. Místo toho použijte remoteObjects.defineType(name, converter).", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "Metoda je nezbytná při volání {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "Volání metody uživatele", 12 | "390c95fb109995a2a126d2edfdb287af": "Došlo k neznámé chybě", 13 | "3a82ba5e35f5918d223ca607648727ca": "Nepodporovaný cíl propojení procesů", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Nelze vyvolat metodu bez adaptéru. Viz {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "Nepodporovaný typ proudu: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "Adaptér REST se již nedodává s vestavěným middlewarem CORS, volba konfigurace {0} již není k dispozici. Další podrobnosti viz {1}.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} již nepodporuje volbu \"{{errorHandler.disableStackTrace}}\". Místo toho použijte volbu \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Nelze analyzovat hodnotu pole zakódované pomocí JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Nelze analyzovat hodnotu objektu zakódované pomocí JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Hodnota není objekt.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Hodnota není bezpečné celé číslo.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Nelze vyvolat metodu bez připojení. Viz {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Nezdařilo se spustit server s: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Chyba serveru", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} se již nepodporuje. Místo toho použijte nová rozhraní API: {1} nebo {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "Volání {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Byla vrácena nebezpečná celočíselná hodnota pro argument '{0}' typu '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "po {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "Metoda neexistuje", 30 | "882f4f76c3359a51746cb7437c324e71": "Chyba: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) již není podporován. Místo toho použijte remoteObjects.defineType(name, converter).", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Některé položky pole nejsou objektem.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Hodnota není řetězec.", 34 | "97132088745bbd7526d5027319ff57e3": "Volání metody na {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Naslouchání na portu {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Neplatný návratový argument {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Hodnota není platné datum.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Nelze potlačit vestavěný typ \"{{file}}\".", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Varování: potlačení typu {0} vzdálená komunikace", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Typy pole s více než jedním typem položky nejsou podporovány. Použije se první typ položky a zbytek bude ignorován.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Neplatný argument {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Hodnota není logickou hodnotou.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Probíhá zpracování neznámého typu {0} vzdálené komunikace jako \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "nepodporovaný typ proudu", 45 | "e5416be29602388d91e3dee81ddddfcf": "Hodnota není pole.", 46 | "e63f0a95675a023883a29671c8c07113": "Nepodporovaný deskriptor proudu, podporovány jsou pouze deskriptory s vlastností \"{{json:true}}\"", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Nelze vytvořit odezvu souboru z {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Nelze vyvolat neznámou metodu: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "musí se poskytnout platná hodnota {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" nemá metodu zpracovávající {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "falsches Kennwort!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Wert ist keine Zahl.", 4 | "224c1872889edc69277dfa73bb3486a4": "Ungültiger Rückgabewert für Argument '{0}' des Typs '{1}': {2}. Empfangener Typ war {3}. ", 5 | "24ef3337f93d26823c496c947b74134b": "Es ist keine Methode für die Verarbeitung von {0} {1} vorhanden", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: Nicht-Root-Rückgabeargument {1} des Typs \"{{file}}\" verworfen ", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} ist ein erforderliches Argument", 8 | "2874eedf72302267612549956af600de": "nicht unterstützte Pipequelle", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) wird nicht mehr unterstützt. Verwenden Sie stattdessen remoteObjects.defineType(name, converter).", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "Methode ist beim Aufrufen von {{invoke()}} erforderlich", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "Aufruf einer Benutzermethode", 12 | "390c95fb109995a2a126d2edfdb287af": "Ein unbekannter Fehler ist aufgetreten", 13 | "3a82ba5e35f5918d223ca607648727ca": "nicht unterstütztes Pipeziel", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Methode kann ohne einen Adapter nicht aufgerufen werden. Siehe {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "nicht unterstützter Datenstromtyp: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "Der REST-Adapter enthält keine integrierte CORS-Middleware mehr. Die Konfigurationsoption {0} ist nicht mehr verfügbar. Für weitere Details siehe {1}.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} unterstützt die Option \"{{errorHandler.disableStackTrace}}\" nicht mehr. Verwenden Sie stattdessen die neue Option \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "JSON-codierter Array-Wert kann nicht geparst werden.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "JSON-codierter Objektwert kann nicht geparst werden.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Wert ist kein Objekt.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Wert ist keine sichere Ganzzahl.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Methode kann ohne eine Verbindung nicht aufgerufen werden. Siehe {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Der Server wurde nicht gestartet mit: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Serverfehler", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} wird nicht mehr unterstützt. Verwenden Sie stattdessen eine der neuen APIs: {1} or {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "Aufruf von {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Nicht sicherer Ganzzahlwert zurückgegeben für Argument '{0}' des Typs '{1}': {2}. ", 28 | "8201d8f57251ab2b647fd91457659ea3": "nach {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "Methode ist nicht vorhanden", 30 | "882f4f76c3359a51746cb7437c324e71": "Fehler: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) wird nicht mehr unterstützt. Verwenden Sie stattdessen remoteObjects.defineType(name, converter).", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Einige der Array-Elemente sind kein Objekt.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Wert ist keine Zeichenfolge.", 34 | "97132088745bbd7526d5027319ff57e3": "Aufruf einer Methode auf {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Empfangsbereit auf Port {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Ungültiges Rückgabeargument {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Wert ist keine gültiges Datum.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Integrierter Typ \"{{file}}\" kann nicht überschrieben werden.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Warnung: Remoting-Typ {0} wird überschrieben", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Array-Typen mit mehreren Elementtypen werden nicht unterstützt. Erster Elementtyp wird verwendet, der Rest ignoriert.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Ungültiges Argument {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Wert ist kein boolescher Wert.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Unbekannter Remoting-Typ {0} wird als \"any\" behandelt", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "nicht unterstützter Datenstromtyp", 45 | "e5416be29602388d91e3dee81ddddfcf": "Wert ist kein Array.", 46 | "e63f0a95675a023883a29671c8c07113": "Nicht unterstützter Datenstromdeskriptor; nur Deskriptoren mit der Eigenschaft \"{{json:true}}\" werden unterstützt", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Es kann keine Dateiantwort aus {0} erstellt werden ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Unbekannte Methode kann nicht aufgerufen werden: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "Eine gültige {{SharedClass}} muss angegeben werden", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" verfügt über keine Methode für die Verarbeitung von {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "bad password!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Value is not a number.", 4 | "224c1872889edc69277dfa73bb3486a4": "Invalid return value for argument '{0}' of type '{1}': {2}. Received type was {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "There is no method to handle {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: discarded non-root return argument {1} of type \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} is a required argument", 8 | "2874eedf72302267612549956af600de": "unsupported pipe source", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) is no longer supported. Use remoteObjects.defineType(name, converter) instead.", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "method is required when calling {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "calling a user method", 12 | "390c95fb109995a2a126d2edfdb287af": "An unknown error occurred", 13 | "3a82ba5e35f5918d223ca607648727ca": "unsupported pipe destination", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Cannot invoke method without an adapter. See {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "unsupported stream type: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "The REST adapter no longer comes with a built-in CORS middleware, the config option {0} is no longer available.See {1} for more details.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} no longer supports \"{{errorHandler.disableStackTrace}}\" option. Use the new option \"{{errorHandler.debug}}\" instead.", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Cannot parse JSON-encoded array value.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Cannot parse JSON-encoded object value.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Value is not an object.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Value is not a safe integer.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Cannot invoke method without a connection. See {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Failed to start server with: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Server error", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} is no longer supported. Use one of the new APIs instead: {1} or {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "calling {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Unsafe integer value returned for argument '{0}' of type '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "after {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "method does not exist", 30 | "882f4f76c3359a51746cb7437c324e71": "Error: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) is no longer supported. Use remoteObjects.defineType(name, converter) instead.", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Some of array items are not an object.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Value is not a string.", 34 | "97132088745bbd7526d5027319ff57e3": "calling a method on {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Listening on port {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Invalid return argument {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Value is not a valid date.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Cannot override built-in \"{{file}}\" type.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Warning: overriding remoting type {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Array types with more than one item type are not supported. Using the first item type and ignoring the rest.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Invalid argument {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Value is not a boolean.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Treating unknown remoting type {0} as \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "unsupported stream type", 45 | "e5416be29602388d91e3dee81ddddfcf": "Value is not an array.", 46 | "e63f0a95675a023883a29671c8c07113": "Unsupported stream descriptor, only descriptors with property \"{{json:true}}\" are supported", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Cannot create a file response from {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Cannot invoke unkown method: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "must provide a valid {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" has no method handling {1} {2}" 51 | } 52 | -------------------------------------------------------------------------------- /intl/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "contraseña incorrecta.", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "El valor no es un número.", 4 | "224c1872889edc69277dfa73bb3486a4": "Valor de retorno no válido para argumento '{0}' de tipo '{1}': {2}. El tipo recibido es {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "No existe ningún método para manejar {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: se ha descartado el argumento de retorno no root {1} de tipo \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} es un argumento obligatorio", 8 | "2874eedf72302267612549956af600de": "origen de canal de comunicación no soportado", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) ya no está soportado. Utilice remoteObjects.defineType(name, converter) en su lugar.", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "el método es necesario al llamar a {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "llamando a un método de usuario", 12 | "390c95fb109995a2a126d2edfdb287af": "Se ha producido un error desconocido", 13 | "3a82ba5e35f5918d223ca607648727ca": "destino de canal de comunicación no soportado", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "No se puede invocar un método sin un adaptador. Consulte {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "tipo de corriente no soportado: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "El adaptador REST ya no viene con un middleware CORS incorporado, la opción de configuración {0} ya no está disponible. Consulte {1} para obtener información más detallada.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} ya no da soporte a la opción \"{{errorHandler.disableStackTrace}}\". Utilice la nueva opción \"{{errorHandler.debug}}\" en su lugar.", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "No se puede analizar el valor de matriz codificado en JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "No se puede analizar el valor de objeto codificado en JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "El valor no es un objeto.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "El valor no es un entero seguro.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "No se puede invocar un método sin una conexión. Consulte {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "No se ha podido iniciar el servidor con: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Error de servidor", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} ya no está soportado. Utilice una de las nuevas API en su lugar: {1} o {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "llamando a {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Valor entero no seguro devuelto para el argumento '{0}' de tipo '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "después de {{speak}}.", 29 | "8594525bc1910e451e37a3b96b9cb04d": "el método no existe", 30 | "882f4f76c3359a51746cb7437c324e71": "Error: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) ya no está soportado. Utilice remoteObjects.defineType(name, converter) en su lugar.", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Algunos de los elementos de matriz no son un objeto.", 33 | "914dc08c2e3001135e112e2d055e5d03": "El valor no es una serie.", 34 | "97132088745bbd7526d5027319ff57e3": "llamando a un método en {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Escuchando en puerto {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Argumento de retorno no válido {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "El valor no es una fecha válida.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "No se puede sobrescribir el tipo \"{{file}}\" incorporado.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Aviso: sobrescribiendo tipo remoto {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "No se admiten los tipos de matriz con más de un tipo de elemento. Se utilizará el primer tipo de elemento y se ignorará el resto.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Argumento no válido {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "El valor no es un booleano.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Tratando el tipo remoto desconocido {0} como \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "tipo de corriente no soportado", 45 | "e5416be29602388d91e3dee81ddddfcf": "El valor no es una matriz.", 46 | "e63f0a95675a023883a29671c8c07113": "Descriptor de corriente no soportado, sólo están soportados los descriptores con la propiedad \"{{json:true}}\"", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "No se puede crear un archivo de respuestas desde {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "No se puede invocar un método desconocido: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "debe proporcionar una {{SharedClass}} válida", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" no tiene ningún método que maneje {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "password errata.", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Il valore non è un numero.", 4 | "224c1872889edc69277dfa73bb3486a4": "Valore di restituzione non valido per l'argomento '{0}' di tipo '{1}': {2}. Il tipo ricevuto era {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Non è presente alcun metodo per gestire {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: l'argomento di restituzione non root {1} di tipo \"{{file}}\" è stato eliminato", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} è un argomento obbligatorio", 8 | "2874eedf72302267612549956af600de": "origine pipe non supportata", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) non è più supportato. Utilizzare remoteObjects.defineType(name, converter).", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "il metodo è obbligatorio quando viene richiamato {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "richiamo di un metodo utente", 12 | "390c95fb109995a2a126d2edfdb287af": "Si è verificato un errore sconosciuto", 13 | "3a82ba5e35f5918d223ca607648727ca": "destinazione pipe non supportata", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Impossibile richiamare il metodo senza un adattatore. Consultare {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "tipo di flusso non supportato: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "L'adattatore REST non viene più fornito con un middleware CORS integrato, l'opzione di configurazione {0} non è più disponibile. Consultare {1} per ulteriori dettagli.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} non supporta più l'opzione \"{{errorHandler.disableStackTrace}}\". Utilizzare la nuova opzione \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Impossibile analizzare il valore dell'array in codifica JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Impossibile analizzare il valore dell'oggetto in codifica JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Il valore non è un oggetto.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Il valore non è un intero sicuro.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Impossibile richiamare il metodo senza una connessione. Consultare {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Impossibile avviare il server con: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Errore del server", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} non è più supportata. Utilizzare una delle nuove API: {1} o {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "richiamo di {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Valore intero non sicuro restituito per l'argomento '{0}' di tipo '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "dopo {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "il metodo non esiste.", 30 | "882f4f76c3359a51746cb7437c324e71": "Errore: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) non è più supportato. Utilizzare remoteObjects.defineType(name, converter).", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Alcuni elementi dell'array non sono un oggetto.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Il valore non è una stringa.", 34 | "97132088745bbd7526d5027319ff57e3": "richiamo di un metodo su {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "In attesa sulla porta {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Argomento di restituzione non valido {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Il valore non è una data valida.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Impossibile sostituire il tipo \"{{file}}\" integrato.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Avvertenza: sostituzione del tipo di comunicazione remota {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "I tipi di array con più di un tipo di elemento non sono supportati. Il primo tipo di elemento viene utilizzato e gli altri vengono ignorati.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Argomento non valido {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Il valore non è un valore booleano.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Il tipo di comunicazione remota sconosciuto {0} viene considerato come \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "tipo di flusso non supportato", 45 | "e5416be29602388d91e3dee81ddddfcf": "Il valore non è un array.", 46 | "e63f0a95675a023883a29671c8c07113": "Descrittore del flusso non supportato, sono supportati solo descrittori con la proprietà \"{{json:true}}\"", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Impossibile creare una risposta file da {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Impossibile richiamare metodo sconosciuto: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "deve fornire una {{SharedClass}} valida", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" non dispone di alcun metodo che gestisce {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "パスワードが間違っています。", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "値は数値ではありません。", 4 | "224c1872889edc69277dfa73bb3486a4": "タイプ '{1}' の引数 '{0}' の戻り値が無効です: {2}。 受け取ったタイプは {3} でした。", 5 | "24ef3337f93d26823c496c947b74134b": "{0} {1} を処理するメソッドがありません", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: タイプ \"{{file}}\" のルート以外の戻り引数 {1} は破棄されました", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} は必須の引数です", 8 | "2874eedf72302267612549956af600de": "パイプのソースがサポートされていません", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) は現在サポートされていません。 代わりに remoteObjects.defineType(name, converter) を使用してください。", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "{{invoke()}} を呼び出すときはメソッドが必要です", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "ユーザー・メソッドの呼び出し", 12 | "390c95fb109995a2a126d2edfdb287af": "不明なエラーが発生しました", 13 | "3a82ba5e35f5918d223ca607648727ca": "パイプの宛先がサポートされていません", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "アダプターなしでメソッドを呼び出すことはできません。 {{RemoteObjects#connect().}} を参照してください", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "サポートされないストリーム・タイプ: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "REST アダプターには組み込みの CORS ミドルウェアが付属しなくなりました。構成オプション {0} は使用できなくなりました。詳しくは、{1} を参照してください。", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} では \"{{errorHandler.disableStackTrace}}\" オプションはサポートされなくなりました。 代わりに新規オプション \"{{errorHandler.debug}}\" を使用してください。", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "JSON エンコードされた配列値を解析できません。", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "JSON エンコードされたオブジェクト値を解析できません。", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "値はオブジェクトではありません。", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "値は安全な整数ではありません。", 22 | "72f32d46e4fee59240fb8c541eecf94c": "接続せずにメソッドを呼び出すことはできません。 {{RemoteObjects#connect().}} を参照してください", 23 | "73082fe621ac741821952223ab257984": "{0} によるサーバーの始動に失敗しました", 24 | "731e024ead4b1b6cdedeb8e74373c451": "サーバー・エラー", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} はサポートされなくなりました。 代わりに新しい API {1} または {2} のいずれかを使用してください", 26 | "794828000e3b4f3c0f21aa01cae5043c": "{0} の呼び出し中", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "タイプ '{1}' の引数 '{0}' に対して安全でない整数値が返されました: {2}。", 28 | "8201d8f57251ab2b647fd91457659ea3": "{{speak}} 後。", 29 | "8594525bc1910e451e37a3b96b9cb04d": "メソッドが存在しません", 30 | "882f4f76c3359a51746cb7437c324e71": "エラー: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) は現在サポートされていません。 代わりに remoteObjects.defineType(name, converter) を使用してください。", 32 | "8fd887d3982016ba1e563ce2de85cbde": "配列項目の一部がオブジェクトではありません。", 33 | "914dc08c2e3001135e112e2d055e5d03": "値はストリングではありません。", 34 | "97132088745bbd7526d5027319ff57e3": "{0} でのメソッドの呼び出し", 35 | "a70394aadda9923fb4d5215ebffd431a": "ポート {0} の listen 中...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "無効な戻り引数 {0}。 ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "値は有効な日付ではありません。", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "組み込みの \"{{file}}\" タイプはオーバーライドできません。", 39 | "b314f6ed6bd74c9169734e8870e69f65": "警告: リモート処理タイプ {0} をオーバーライドしています", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "複数の項目タイプを使用する配列型はサポートされません。 最初の項目タイプが使用され、残りは無視されます。", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "引数 {0} は無効です。 ", 42 | "ce5999734f432272be2cf2aabb6a715f": "値はブール値ではありません。", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "不明なリモート処理タイプ {0} を \"any\" として処理します", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "サポートされないストリーム・タイプ", 45 | "e5416be29602388d91e3dee81ddddfcf": "値は配列ではありません。", 46 | "e63f0a95675a023883a29671c8c07113": "サポートされないストリーム記述子です。プロパティーが \"{{json:true}}\" の記述子のみがサポートされます", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "{0} からファイル応答を作成できません ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "不明なメソッド {0} を呼び出すことができません", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "有効な {{SharedClass}} を指定する必要があります", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" には {1} {2} を処理するメソッドがありません" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "잘못된 비밀번호입니다!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "값이 숫자가 아닙니다.", 4 | "224c1872889edc69277dfa73bb3486a4": "'{1}' 유형의 인수 '{0}'에 리턴 값이 올바르지 않음: {2}. 받은 유형은 {3}입니다.", 5 | "24ef3337f93d26823c496c947b74134b": "{0} {1}을(를) 처리할 메소드가 없음", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: \"{{file}}\" 유형의 비루트 리턴 인수 {1}을(를) 버림", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0}이(가) 필수 인수임", 8 | "2874eedf72302267612549956af600de": "지원되지 않는 파이프 소스", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn)는 더 이상 지원되지 않습니다. 대신 remoteObjects.defineType(name, converter)을 사용하십시오.", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "{{invoke()}}을(를) 호출하는 경우 메소드가 필수임", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "사용자 메소드 호출", 12 | "390c95fb109995a2a126d2edfdb287af": "알 수 없는 오류 발생", 13 | "3a82ba5e35f5918d223ca607648727ca": "지원되지 않는 파이프 대상", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "어댑터 없이 메소드를 호출할 수 없습니다. {{RemoteObjects#connect().}} 참조", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "지원되지 않는 스트림 유형: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "REST 어댑터에 기본 제공 CORS 미들웨어가 더 이상 제공되지 않으며 구성 옵션 {0}도 더 이상 사용할 수 없습니다. 세부사항은 {1}의 내용을 참조하십시오.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}}이(가) 더 이상 \"{{errorHandler.disableStackTrace}}\" 옵션을 지원하지 않습니다. 새 옵션 \"{{errorHandler.debug}}\"을(를) 대신 사용하십시오.", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "JSON 인코딩 방식의 배열 값을 구문 분석할 수 없습니다.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "JSON 인코딩 방식의 오브젝트 값을 구문 분석할 수 없습니다.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "값이 오브젝트가 아닙니다.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "값이 안전한 정수가 아닙니다.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "연결 없이 메소드를 호출할 수 없습니다. {{RemoteObjects#connect().}} 참조", 23 | "73082fe621ac741821952223ab257984": "다음으로 서버를 시작하는 데 실패함: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "서버 오류", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0}은(는) 더 이상 지원되지 않습니다. 대신 새 API 중 하나를 사용하십시오. {1} 또는 {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "{0} 호출 중", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "'{1}': {2} 유형의 인수 '{0}'에 안전하지 않는 정수 값이 리턴되었습니다.", 28 | "8201d8f57251ab2b647fd91457659ea3": "{{speak}} 이후!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "메소드가 없음", 30 | "882f4f76c3359a51746cb7437c324e71": "오류: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn)은 더 이상 지원되지 않습니다. 대신 remoteObjects.defineType(name, converter)을 사용하십시오.", 32 | "8fd887d3982016ba1e563ce2de85cbde": "일부 배열 항목이 오브젝트가 아닙니다.", 33 | "914dc08c2e3001135e112e2d055e5d03": "값이 문자열이 아닙니다.", 34 | "97132088745bbd7526d5027319ff57e3": "{0}에서 메소드 호출 중", 35 | "a70394aadda9923fb4d5215ebffd431a": "포트 {0}에서 청취 중입니다...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "리턴 인수 {0}이(가) 올바르지 않습니다. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "값이 올바른 날짜가 아닙니다.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "기본 제공 \"{{file}}\" 유형을 대체할 수 없습니다.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "경고: 원격 유형 {0} 대체 중", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "항목 유형이 둘 이상인 배열 유형은 지원되지 않습니다. 첫 번째 항목 유형만 사용하고 나머지는 무시합니다.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "{0} 인수가 올바르지 않습니다. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "값이 부울이 아닙니다.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "알 수 없는 원격 유형 {0}을(를) \"any\"로 처리", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "지원되지 않는 스트림 유형", 45 | "e5416be29602388d91e3dee81ddddfcf": "값이 배열이 아닙니다.", 46 | "e63f0a95675a023883a29671c8c07113": "지원되지 않는 스트림 디스크립터입니다. \"{{json:true}}\" 특성을 가진 디스크립터만 지원됩니다.", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "{0}에서 파일 응답을 작성할 수 없음 ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "알 수 없는 메소드를 호출할 수 없음: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "올바른 {{SharedClass}}을(를) 제공해야 함", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\"에 {1} {2}을(를) 처리 중인 메소드가 없음" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "onjuist wachtwoord!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Waarde is geen getal. ", 4 | "224c1872889edc69277dfa73bb3486a4": "Ongeldige retourwaarde voor argument '{0}' van type '{1}': {2}. Ontvangen type is {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Er is geen methode voor het verwerken van {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: niet-root retourargument {1} van type \"{{file}}\" verwijderd", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} is een verplicht argument", 8 | "2874eedf72302267612549956af600de": "niet-ondersteunde bron van pipe", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) wordt niet meer ondersteund. Gebruik in plaats daarvan remoteObjects.defineType(naam, converter). ", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "methode vereist bij aanroepen van {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "gebruikersmethode wordt aangeroepen", 12 | "390c95fb109995a2a126d2edfdb287af": "Er is een onbekende fout opgetreden", 13 | "3a82ba5e35f5918d223ca607648727ca": "Niet-ondersteunde bestemming van pipe", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Methode kan niet worden opgeroepen zonder adapter. Zie {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "niet-ondersteund type gegevensstroom: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "De REST-adapter wordt niet meer geleverd met ingebouwde CORS-middleware. De configuratieoptie {0} is niet meer beschikbaar. Zie {1} voor meer informatie. ", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} biedt geen ondersteuning meer voor de optie \"{{errorHandler.disableStackTrace}}\". Gebruik in plaats daarvan de nieuwe optie \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Waarde voor JSON-gecodeerde array kan niet worden ontleed. ", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Waarde voor JSON-gecodeerd object kan niet worden ontleed. ", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Waarde is geen object. ", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Waarde is geen veilig geheel getal. ", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Methode kan niet worden opgeroepen zonder verbinding. Zie {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Fout bij starten van server met: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Serverfout", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} wordt niet meer ondersteund. Gebruik in plaats daarvan een van de nieuwe API's: {1} of {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "{0} wordt aangeroepen", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Onveilig geheel getal geretourneerd voor argument '{0}' van type '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "na {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "methode bestaat niet", 30 | "882f4f76c3359a51746cb7437c324e71": "Fout: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType (naam, fn) wordt niet meer ondersteund. Gebruik in plaats daarvan remoteObjects.defineType(naam, converter). ", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Bepaalde array-items zijn geen objecten. ", 33 | "914dc08c2e3001135e112e2d055e5d03": "Waarde is geen tekenreeks. ", 34 | "97132088745bbd7526d5027319ff57e3": "methode aanroepen voor {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Er wordt geluisterd op poort {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Ongeldig retourargument {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Waarde is geen geldige datum. ", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Ingebouwd type \"{{file}}\" kan niet worden vervangen (override).", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Waarschuwing: remoting type {0} wordt vervangen (override)", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Arraytypes met meer dan een itemtype worden niet ondersteund. Het eerste itemtype wordt gebruikt, de rest owrdt genegeerd. ", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Ongeldig argument {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Waarde is geen booleaanse waarde. ", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Onbekend remoting type {0} wordt behandeld als \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "niet-ondersteund type gegevensstroom", 45 | "e5416be29602388d91e3dee81ddddfcf": "Waarde is geen array. ", 46 | "e63f0a95675a023883a29671c8c07113": "Niet-ondersteunde descriptor voor gegevensstroom; alleen descriptors met eigenschap \"{{json:true}}\" worden ondersteund", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Kan geen bestandsrespons maken op basis van {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Onbekende methode kan niet gestart worden: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "U moet een geldige {{SharedClass}} opgeven", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" heeft geen methodeverwerking van het type {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/pl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "błędne hasło!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Wartość nie jest liczbą.", 4 | "224c1872889edc69277dfa73bb3486a4": "Niepoprawna wartość zwracana dla argumentu '{0}' typu '{1}': {2}. Odebrano typ {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Brak metody do obsługi {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: usunięto inny niż główny argument zwracany {1} typu \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} jest argumentem wymaganym", 8 | "2874eedf72302267612549956af600de": "nieobsługiwane źródło potoku", 9 | "2ef57c77f9541779bf797f4347525da4": "Metoda RemoteObjects.convert(name, fn) nie jest już obsługiwana. Zamiast niej użyj metody remoteObjects.defineType(name, converter).", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "metoda jest wymagana podczas wywoływania metody {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "wywoływanie metody użytkownika", 12 | "390c95fb109995a2a126d2edfdb287af": "Wystąpił nieznany błąd", 13 | "3a82ba5e35f5918d223ca607648727ca": "nieobsługiwany cel potoku", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Nie można wywołać metody bez adaptera. Więcej informacji na ten temat zawiera sekcja {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "nieobsługiwany typ strumienia: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "Adapter REST nie jest już dostarczany z wbudowanym oprogramowaniem pośrednim CORS. Opcja konfiguracji {0} nie jest już dostępna. Więcej informacji na ten temat zawiera sekcja {1}.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} nie obsługuje już opcji \"{{errorHandler.disableStackTrace}}\". Zamiast niej użyj nowej opcji \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Nie można przeanalizować wartości tablicy zakodowanej w formacie JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Nie można przeanalizować wartości obiektu zakodowanego w formacie JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Wartość nie jest obiektem.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Wartość nie jest bezpieczną liczbą całkowitą.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Nie można wywołać metody bez połączenia. Więcej informacji na ten temat zawiera sekcja {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Nie powiodła się próba uruchomienia serwera z: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Błąd serwera", 25 | "7521ee0eab6fac3f20b8635417d55887": "Zaprzestano obsługi {0}. Należy użyć jednej z nowych funkcji API: {1} lub {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "wywoływanie {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Zwrócono niebezpieczną liczbę całkowitą dla argumentu '{0}' typu '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "po {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "metoda nie istnieje", 30 | "882f4f76c3359a51746cb7437c324e71": "Błąd: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "Metoda RemoteObjects.defineType(name, fn) nie jest już obsługiwana. Zamiast niej użyj metody remoteObjects.defineType(name, converter).", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Niektóre elementy tablicy nie są obiektami.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Wartość nie jest łańcuchem.", 34 | "97132088745bbd7526d5027319ff57e3": "wywoływanie metody na {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Nasłuchiwanie na porcie {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Niepoprawny argument zwracany {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Wartość nie jest poprawną datą.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Nie można przesłonić wbudowanego typu \"{{file}}\".", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Ostrzeżenie: przesłonięcie typu zdalnego {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Typy tablicowe z więcej niż jednym typem elementu nie są obsługiwane. Zostanie użyty pierwszy typ elementu, a reszta zostanie zignorowana.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Niepoprawny argument {0}.", 42 | "ce5999734f432272be2cf2aabb6a715f": "Wartość nie jest boolowska.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Traktowanie nieznanego typu zdalnego {0} jako \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "nieobsługiwany typ strumienia", 45 | "e5416be29602388d91e3dee81ddddfcf": "Wartość nie jest tablicą.", 46 | "e63f0a95675a023883a29671c8c07113": "Nieobsługiwany deskryptor strumienia; obsługiwane są tylko deskryptory z właściwością \"{{json:true}}\"", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Nie można utworzyć pliku odpowiedzi z {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Nie można wywołać nieznanej metody: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "należy podać poprawną wartość: {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" nie ma obsługi metod {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/pt/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "senha inválida!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "O valor não é um número.", 4 | "224c1872889edc69277dfa73bb3486a4": "Valor de retorno inválido para argumento '{0}' do tipo '{1}': {2}. O tipo recebido era {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Não há método para manipular {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: descartou argumento de retorno não raiz {1} do tipo \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} é um argumento obrigatório", 8 | "2874eedf72302267612549956af600de": "origem de canal não suportada", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) não é mais suportado. Use remoteObjects.defineType(name, converter) em vez disso.", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "método é necessário ao chamar {{invoke()}}", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "chamando um método do usuário", 12 | "390c95fb109995a2a126d2edfdb287af": "Ocorreu um erro desconhecido", 13 | "3a82ba5e35f5918d223ca607648727ca": "destino de canal não suportado", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Não é possível chamar método sem um adaptador. Consulte {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "tipo de fluxo não suportado: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "O adaptador REST não vem mais com um middleware CORS integrado, a opção de configuração {0} não está mais disponível. Consulte {1} para obter mais detalhes.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} não suporta mais a opção \"{{errorHandler.disableStackTrace}}\". Use a nova opção \"{{errorHandler.debug}}\" no lugar.", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Não é possível analisar o valor de matriz codificado por JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Não é possível analisar o valor de objeto codificado por JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "O valor não é um objeto.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "O valor não é um número inteiro seguro.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Não é possível chamar método sem uma conexão. Consulte {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Falha ao iniciar servidor com: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Erro do Servidor", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} não é mais suportado. Em vez disso, use uma das novas APIs: {1} ou {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "chamando {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Valor de número inteiro inseguro retornado para o argumento '{0}' do tipo '{1}': {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "após {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "método não existe", 30 | "882f4f76c3359a51746cb7437c324e71": "Erro: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) não é mais suportado. Use remoteObjects.defineType(name, converter) em vez disso.", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Alguns itens da matriz não são um objeto.", 33 | "914dc08c2e3001135e112e2d055e5d03": "O valor não é uma sequência.", 34 | "97132088745bbd7526d5027319ff57e3": "chamando um método em {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Recebendo na porta {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Argumento de retorno inválido {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "O valor não é uma data válida.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Não é possível substituir o tipo \"{{file}}\" integrado.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Aviso: substituindo tipo remoto {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Tipos de matriz com mais de um tipo de item não são suportados. Usando o primeiro tipo de item e ignorando o restante.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Argumento inválido {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "O valor não é um booleano.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Tratando o tipo remoto desconhecido {0} como \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "tipo de fluxo não suportado", 45 | "e5416be29602388d91e3dee81ddddfcf": "O valor não é uma matriz.", 46 | "e63f0a95675a023883a29671c8c07113": "Descritor de fluxos não suportado, somente descritores com a propriedade \"{{json:true}}\" são suportados", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Não é possível criar uma resposta de arquivo a partir de {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Não é possível chamar o método desconhecido: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "deve-se fornecer um {{SharedClass}} válido", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" não possui manipulação de método {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "Неверный пароль!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Значение не является числом.", 4 | "224c1872889edc69277dfa73bb3486a4": "Недопустимое возвращаемое значение для аргумента '{0}' с типом '{1}': {2}. Получен тип: {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Нет методов для обработки {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: аннулирован некорневой аргумент возврата {1} с типом \"{{file}}\"", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} является обязательным аргументом", 8 | "2874eedf72302267612549956af600de": "неподдерживаемый источник конвейера", 9 | "2ef57c77f9541779bf797f4347525da4": "Функция RemoteObjects.convert(name, fn) более не поддерживается. Используйте вместо нее remoteObjects.defineType(name, converter).", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "При вызове {{invoke()}} необходимо указать метод", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "Вызов пользовательского метода", 12 | "390c95fb109995a2a126d2edfdb287af": "Возникла неизвестная ошибка", 13 | "3a82ba5e35f5918d223ca607648727ca": "Неподдерживаемый целевой объект конвейера", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Не удается вызвать метод без адаптера. См. {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "Неподдерживаемый тип потока: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "Адаптер REST больше не поставляется в комплекте со встроенным промежуточным ПО CORS, опция конфигурации {0} больше не доступна. Дополнительная информация приведена в разделе {1}.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} больше не поддерживает опцию \"{{errorHandler.disableStackTrace}}\". Используйте вместо нее новую опцию \"{{errorHandler.debug}}\".", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "Не удалось проанализировать значение массива в формате JSON.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "Не удалось проанализировать значение объекта в формате JSON.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Значение не является объектом.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Значение не является безопасным целым числом.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Невозможно вызвать метод без соединения. См. {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Не удалось запустить сервер с {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Ошибка сервера", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} больше не поддерживается. Используйте один из следующих новых API: {1} или {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "вызов {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "Для аргумента '{0}' с типом '{1}' возвращено небезопасное целочисленное значение: {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "после {{speak}}!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "Метод не существует", 30 | "882f4f76c3359a51746cb7437c324e71": "Ошибка: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "Функция RemoteObjects.defineType(name, fn) более не поддерживается. Используйте вместо нее remoteObjects.defineType(name, converter).", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Некоторые из элементов массива не являются объектами.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Значение не является строкой.", 34 | "97132088745bbd7526d5027319ff57e3": "вызов метода в {0}", 35 | "a70394aadda9923fb4d5215ebffd431a": "Обработка запросов на порту {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Недопустимый аргумент возврата {0}. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Значение не является допустимой датой.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Не удалось переопределить встроенный тип \"{{file}}\".", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Предупреждение: переопределение типа удаленного соединения {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Типы массивов с несколькими типами элементов не поддерживаются. Используется первый тип элемента, остальные типы будут проигнорированы.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Недопустимый аргумент {0}. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Значение не является булевским значением.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Неизвестный тип удаленного соединения {0} трактуется как \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "неподдерживаемый тип потока", 45 | "e5416be29602388d91e3dee81ddddfcf": "Значение не является массивом.", 46 | "e63f0a95675a023883a29671c8c07113": "Неподдерживаемый дескриптор потока, поддерживаются только дескрипторы со свойством \"{{json:true}}\"", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "Не удалось создать ответ на файл из {0} ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Не удалось вызвать неизвестный метод: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "необходимо указать допустимый {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" не содержит метод для обработки {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/tr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "hatalı parola!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "Değer bir sayı değil.", 4 | "224c1872889edc69277dfa73bb3486a4": "'{1}' tipindeki '{0}' bağımsız değişkeni için geçersizdönüş değeri: {2}. Alınan tip: {3}.", 5 | "24ef3337f93d26823c496c947b74134b": "Şu öğeyi işleyecek yöntem yok: {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}: \"{{file}}\" tipindeki kök olmayan dönüş bağımsız değişkeni {1} atıldı", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} zorunlu bir bağımsız değişkendir", 8 | "2874eedf72302267612549956af600de": "desteklenmeyen veri bağlantısı kaynağı", 9 | "2ef57c77f9541779bf797f4347525da4": "RemoteObjects.convert(name, fn) artık desteklenmiyor. Bunun yerine remoteObjects.defineType(name, converter) kullanın.", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "{{invoke()}} çağrılırken yöntem gereklidir", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "bir kullanıcı yöntemi çağrılıyor", 12 | "390c95fb109995a2a126d2edfdb287af": "Bilinmeyen bir hata oluştu", 13 | "3a82ba5e35f5918d223ca607648727ca": "desteklenmeyen veri bağlantısı hedefi", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "Bağdaştırıcı olmadan yöntem çağrılamaz. Bakınız: {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "desteklenmeyen akış tipi: {0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "REST bağdaştırıcısı artık yerleşik bir CORS ara katman yazılımıyla birlikte gelmez; {0} yapılandırma seçeneği artık kullanılamıyor. Daha fazla ayrıntı için bkz. {1}.", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} artık \"{{errorHandler.disableStackTrace}}\" seçeneğini desteklemiyor. Onun yerine, yeni \"{{errorHandler.debug}}\" seçeneğini kullanın.", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "JSON kodlu dizi değeri ayrıştırılamıyor.", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "JSON kodlu nesne değeri ayrıştırılamıyor.", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "Değer bir nesne değil.", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "Değer güvenli bir tamsayı değil.", 22 | "72f32d46e4fee59240fb8c541eecf94c": "Bağlantı olmadan yöntem çağrılamaz. Bakınız: {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "Şununla sunucu başlatılamadı: {0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "Sunucu hatası", 25 | "7521ee0eab6fac3f20b8635417d55887": "{0} artık desteklenmiyor. Bunun yerine yeni API'lerden birini kullanın: {1} ya da {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "{0} çağrılıyor", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "'{1}' tipindeki '{0}' bağımsız değişkeni için güvenli olmayan tamsayı değeri döndürüldü: {2}.", 28 | "8201d8f57251ab2b647fd91457659ea3": "{{speak}} sonrasında!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "yöntem yok", 30 | "882f4f76c3359a51746cb7437c324e71": "Hata: {0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "RemoteObjects.defineType(name, fn) artık desteklenmiyor. Bunun yerine remoteObjects.defineType(name, converter) kullanın.", 32 | "8fd887d3982016ba1e563ce2de85cbde": "Bazı dizi öğeleri nesne değil.", 33 | "914dc08c2e3001135e112e2d055e5d03": "Değer bir dize değil.", 34 | "97132088745bbd7526d5027319ff57e3": "{0} üzerinde bir yöntem çağrılıyor", 35 | "a70394aadda9923fb4d5215ebffd431a": "{0} kapısında dinleniyor...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "Geçersiz {0} bağımsız değişkeni döndürüldü. ", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "Değer geçerli bir tarih değil.", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "Yerleşik \"{{file}}\" tipi geçersiz kılınamıyor.", 39 | "b314f6ed6bd74c9169734e8870e69f65": "Uyarı: {0} uzaktan iletişim tipi geçersiz kılınıyor", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "Birden fazla öğe tipi içeren dizi tipleri desteklenmez. Birinci öğe tipi kullanılıyor, geri kalanı yoksayılıyor.", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "Geçersiz {0} bağımsız değişkeni. ", 42 | "ce5999734f432272be2cf2aabb6a715f": "Değer bir boole değil.", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "Bilinmeyen {0} uzaktan iletişim tipi \"any\" olarak değerlendiriliyor", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "desteklenmeyen akış tipi", 45 | "e5416be29602388d91e3dee81ddddfcf": "Değer bir dizi değil.", 46 | "e63f0a95675a023883a29671c8c07113": "Desteklenmeyen akış tanımlayıcı, yalnızca \"{{json:true}}\" özelliği olan tanımlayıcılar desteklenir", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "{0} öğesinden dosya yanıtı yaratılamaz ", 48 | "ec36540c2f78bc342430b0dd9651c23b": "Bilinmeyen yöntem çağrılamaz: {0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "geçerli bir {{SharedClass}} belirtmelidir", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" içinde {1} {2} öğesini işleyecek yöntem yok" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/zh-Hans/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "错误密码!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "值不是数字。", 4 | "224c1872889edc69277dfa73bb3486a4": "针对类型为“{1}”的自变量“{0}”返回的值无效:{2}。收到的类型为 {3}。", 5 | "24ef3337f93d26823c496c947b74134b": "不存在用于处理 {0} {1} 的方法", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}:丢弃类型为“{{file}}”的非 root 返回自变量 {1}", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} 是必需的自变量", 8 | "2874eedf72302267612549956af600de": "不受支持的管道源", 9 | "2ef57c77f9541779bf797f4347525da4": "不再支持 RemoteObjects.convert(name, fn)。请改为使用 remoteObjects.defineType(name, converter)。", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "在调用 {{invoke()}} 时方法是必需的", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "正在调用用户方法", 12 | "390c95fb109995a2a126d2edfdb287af": "发生未知错误", 13 | "3a82ba5e35f5918d223ca607648727ca": "不受支持的管道目标", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "在没有适配器的情况下,无法调用方法。请参阅 {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "不受支持的流类型:{0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "REST 适配器不再随附内置的 CORS 中间件,配置选项 {0} 不再可用。请参阅 {1} 以获取更多详细信息。", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} 不再支持“{{errorHandler.disableStackTrace}}”选项。改用新选项“{{errorHandler.debug}}”。", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "无法解析 JSON 编码的数组值。", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "无法解析 JSON 编码的对象值。", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "值不是对象。", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "值不是安全的整数。", 22 | "72f32d46e4fee59240fb8c541eecf94c": "在没有连接的情况下,无法调用方法。请参阅 {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "无法使用以下项启动服务器:{0}", 24 | "731e024ead4b1b6cdedeb8e74373c451": "服务器错误", 25 | "7521ee0eab6fac3f20b8635417d55887": "不再支持 {0}。请改为使用以下新 API 之一:{1} 或 {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "正在调用 {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "针对类型为“{1}”的自变量“{0}”返回的整数值不安全:{2}。", 28 | "8201d8f57251ab2b647fd91457659ea3": "在 {{speak}} 之后!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "方法不存在", 30 | "882f4f76c3359a51746cb7437c324e71": "错误:{0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "不再支持 RemoteObjects.defineType(name, fn)。请改为使用 remoteObjects.defineType(name, converter)。", 32 | "8fd887d3982016ba1e563ce2de85cbde": "部分数组项不是对象。", 33 | "914dc08c2e3001135e112e2d055e5d03": "值不是字符串。", 34 | "97132088745bbd7526d5027319ff57e3": "正在调用 {0} 上的方法", 35 | "a70394aadda9923fb4d5215ebffd431a": "正在侦听端口 {0}...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "返回自变量 {0} 无效。", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "值不是有效日期。", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "无法覆盖内置“{{file}}”类型。", 39 | "b314f6ed6bd74c9169734e8870e69f65": "警告:正在覆盖远程类型 {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "不支持包含多种项类型的数组类型。请使用第一种项类型,并忽略其余项类型。", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "自变量 {0} 无效。", 42 | "ce5999734f432272be2cf2aabb6a715f": "值不是布尔值。", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "将未知远程类型 {0} 作为“any”来处理", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "不受支持的流类型", 45 | "e5416be29602388d91e3dee81ddddfcf": "值不是数组。", 46 | "e63f0a95675a023883a29671c8c07113": "不受支持的流描述符,仅支持包含属性“{{json:true}}”的描述符", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "无法从 {0} 创建文件响应", 48 | "ec36540c2f78bc342430b0dd9651c23b": "无法调用未知方法:{0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "必须提供有效的 {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}}“{0}”无处理 {1} {2} 的方法" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /intl/zh-Hant/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "034e10de9a8cf23202573954b4954a71": "密碼不正確!", 3 | "1522cc23b3b53319de2e8d0e8e9c21cf": "值不是數字。", 4 | "224c1872889edc69277dfa73bb3486a4": "對於類型為 '{1}' 的引數 '{0}',回覆值無效:{2}。收到的類型是 {3}。", 5 | "24ef3337f93d26823c496c947b74134b": "沒有方法可處理 {0} {1}", 6 | "256a66d8525fe73b930069e8f3cf46c6": "{0}:已捨棄類型為 \"{{file}}\" 的非根傳回引數 {1}", 7 | "25b2cd672b116d35ce74f47b24bb7c69": "{0} 是必要引數", 8 | "2874eedf72302267612549956af600de": "不受支援的管線來源", 9 | "2ef57c77f9541779bf797f4347525da4": "不再支援 RemoteObjects.convert(name, fn)。請改用 remoteObjects.defineType(name, converter)。", 10 | "3279cb237ed27b3a23d3c41fec2000a0": "呼叫 {{invoke()}} 時需要方法", 11 | "36bcd0e05c475a5daec9025d5c2f3812": "正在呼叫使用者方法", 12 | "390c95fb109995a2a126d2edfdb287af": "發生不明錯誤", 13 | "3a82ba5e35f5918d223ca607648727ca": "不受支援的管線目的地", 14 | "3d7bf522925c9f1e6487b063dcf1a7e6": "沒有配接器,無法呼叫方法。請參閱 {{RemoteObjects#connect().}}", 15 | "484743a7b326960a7fff8a66b4bcb5c6": "不受支援的串流類型:{0}", 16 | "4b97d323f97a564a962afbc4930ec37b": "REST 配接卡不再提供內建 CORS 中介軟體,配置選項 {0} 不再可用。請參閱 {1},以取得更多詳細資料。", 17 | "4f3a8081cfc47facdf4aa48899e82c56": "{{strong-remoting}} 不再支援 \"{{errorHandler.disableStackTrace}}\" 選項。請改用新的選項 \"{{errorHandler.debug}}\"。", 18 | "57d11c7de269eaa7a96ece3c63e84a0b": "無法剖析 JSON 編碼的陣列值。", 19 | "5a30ac9746c4cae5a09d541b79ea5f90": "無法剖析 JSON 編碼的物件值。", 20 | "6f6a60262d2f0de3ef632e57058aeed1": "值不是物件。", 21 | "6fc5bc3205d50c8df9c0bf3822e9fa13": "值不是安全的整數。", 22 | "72f32d46e4fee59240fb8c541eecf94c": "沒有連線,無法呼叫方法。請參閱 {{RemoteObjects#connect().}}", 23 | "73082fe621ac741821952223ab257984": "無法使用 {0} 來啟動伺服器", 24 | "731e024ead4b1b6cdedeb8e74373c451": "伺服器錯誤", 25 | "7521ee0eab6fac3f20b8635417d55887": "不再支援 {0}。請改用其中一個新的 API:{1} 或 {2}", 26 | "794828000e3b4f3c0f21aa01cae5043c": "正在呼叫 {0}", 27 | "7d93ca23a76f5659ffcf62b0aa8eba30": "對於類型為 '{1}' 的引數 '{0}',傳回不安全的整數值:{2}。", 28 | "8201d8f57251ab2b647fd91457659ea3": "{{speak}} 之後!", 29 | "8594525bc1910e451e37a3b96b9cb04d": "方法不存在", 30 | "882f4f76c3359a51746cb7437c324e71": "錯誤:{0}", 31 | "8dff637e4c765591ee63a59e898b7a0e": "不再支援 RemoteObjects.defineType(name, fn)。請改用 remoteObjects.defineType(name, converter)。", 32 | "8fd887d3982016ba1e563ce2de85cbde": "部分陣列項目不是物件。", 33 | "914dc08c2e3001135e112e2d055e5d03": "值不是字串。", 34 | "97132088745bbd7526d5027319ff57e3": "正在 {0} 上呼叫方法", 35 | "a70394aadda9923fb4d5215ebffd431a": "正在埠 {0} 上接聽...", 36 | "aa030253c5d24f0f083bb4406a8d2ec8": "傳回引數 {0} 無效。", 37 | "aa13189caf3af7ea0d3405bfcbe3a1e2": "值不是有效的日期。", 38 | "ab30fc28acd67477c0be3a181c0d3b68": "無法置換內建 \"{{file}}\" 類型。", 39 | "b314f6ed6bd74c9169734e8870e69f65": "警告:正置換遠端類型 {0}", 40 | "c17c9e82b76a125b9aab55b00fdb0a23": "不支援含有多個項目類型的陣列類型。將使用第一個項目類型並忽略剩餘的項目類型。", 41 | "c811151ab9e3cc0b46ea53b3fe97f077": "引數 {0} 無效。", 42 | "ce5999734f432272be2cf2aabb6a715f": "值不是布林值。", 43 | "cfb44a59b58ff71f76077d3d23f4c542": "不明遠端類型 {0} 將視為 \"any\"", 44 | "e25a1cc5ea3518033c173fbe2cfaed69": "不受支援的串流類型", 45 | "e5416be29602388d91e3dee81ddddfcf": "值不是陣列。", 46 | "e63f0a95675a023883a29671c8c07113": "不受支援的串流描述子,僅支援內容為 \"{{json:true}}\" 的描述子", 47 | "e9c8cd93bef08e1bfb9f0ef78f87010a": "無法從 {0} 建立檔案回應", 48 | "ec36540c2f78bc342430b0dd9651c23b": "無法呼叫不明方法:{0}", 49 | "f63af79ff8a01dcd636897df49f5c1fb": "必須提供有效的 {{SharedClass}}", 50 | "fa1a4e25d3c11b110758643419eefcbf": "{{Shared class}} \"{0}\" 沒有方法可處理 {1} {2}" 51 | } 52 | 53 | -------------------------------------------------------------------------------- /lib/context-base.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const assert = require('assert'); 9 | const EventEmitter = require('events').EventEmitter; 10 | const TypeRegistry = require('./type-registry'); 11 | const inherits = require('util').inherits; 12 | 13 | module.exports = ContextBase; 14 | 15 | /** 16 | * A base class for all Context instances 17 | */ 18 | function ContextBase(method, typeRegistry) { 19 | // NOTE(bajtos) we are not asserting via "instanceof" to allow 20 | // multiple copies of strong-remoting to cooperate together 21 | assert(method && typeof method === 'object', 22 | 'method must be a SharedClass instance'); 23 | assert(typeRegistry && typeof typeRegistry === 'object', 24 | 'typeRegistry must be a TypeRegistry instance'); 25 | 26 | EventEmitter.call(this); 27 | 28 | this.method = method; 29 | this.typeRegistry = typeRegistry; 30 | } 31 | 32 | inherits(ContextBase, EventEmitter); 33 | 34 | ContextBase.prototype.getScope = function() { 35 | // Static methods are invoked on the constructor (this = constructor fn) 36 | // Prototype methods are invoked on the instance (this = instance) 37 | const method = this.method; 38 | return this.instance || 39 | method.ctor || 40 | method.sharedMethod && method.sharedMethod.ctor; 41 | }; 42 | 43 | ContextBase.prototype.setReturnArgByName = function(name, value) { 44 | // no-op 45 | }; 46 | -------------------------------------------------------------------------------- /lib/exports-helper.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | /*! 9 | * Expose `ExportsHelper`. 10 | */ 11 | module.exports = ExportsHelper; 12 | 13 | /*! 14 | * Module dependencies. 15 | */ 16 | const debug = require('debug')('strong-remoting:exports-helper'); 17 | 18 | /*! 19 | * Constants 20 | */ 21 | const PASSTHROUGH_OPTIONS = ['http', 'description', 'notes']; 22 | 23 | /** 24 | * @class A wrapper to make manipulating the exports object easier. 25 | * 26 | * @constructor 27 | * Create a new `ExportsHelper` with the given `options`. 28 | */ 29 | 30 | function ExportsHelper(obj) { 31 | if (!(this instanceof ExportsHelper)) { 32 | return new ExportsHelper(obj); 33 | } 34 | 35 | this._obj = obj; 36 | } 37 | 38 | /** 39 | * Sets a value at any path within the exports object. 40 | */ 41 | ExportsHelper.prototype.setPath = setPath; 42 | function setPath(path, value) { 43 | const self = this; 44 | let obj = self._obj; 45 | const split = path.split('.'); 46 | const name = split.pop(); 47 | 48 | split.forEach(function(key) { 49 | if (!obj[key]) { 50 | obj[key] = {}; 51 | } 52 | 53 | obj = obj[key]; 54 | }); 55 | 56 | debug('Setting %s to %s', path, value); 57 | obj[name] = value; 58 | 59 | return self; 60 | } 61 | 62 | /** 63 | * Exports a constructor ("type") with the provided options. 64 | */ 65 | ExportsHelper.prototype.addType = type; 66 | ExportsHelper.prototype.type = type; 67 | function type(fn, options) { 68 | const self = this; 69 | const path = options.path || options.name || fn.name || null; 70 | let sharedCtor = options.sharedCtor || null; 71 | const accepts = options.accepts || null; 72 | 73 | if (!path) { 74 | // TODO: Error. 75 | return self; 76 | } 77 | 78 | if (!sharedCtor) { 79 | // TODO(schoon) - This shouldn't be thought of (or named) as a "shared 80 | // constructor". Instead, this is the lazy find/create sl-remoting uses when 81 | // a prototype method is called. `getInstance`? `findOrCreate`? `load`? 82 | sharedCtor = function() { 83 | const _args = [].slice.call(arguments); 84 | _args.pop()(null, fn.apply(null, _args)); 85 | }; 86 | } 87 | 88 | if (!sharedCtor.accepts) { 89 | sharedCtor.accepts = accepts; 90 | } 91 | 92 | // This is required because sharedCtors are called just like any other 93 | // remotable method. However, you always expect the instance and nothing else. 94 | if (!sharedCtor.returns) { 95 | sharedCtor.returns = {type: 'object', root: true}; 96 | } 97 | 98 | PASSTHROUGH_OPTIONS.forEach(function(key) { 99 | if (options[key]) { 100 | sharedCtor[key] = options[key]; 101 | } 102 | }); 103 | 104 | self.setPath(path, fn); 105 | fn.shared = true; 106 | fn.sharedCtor = sharedCtor; 107 | 108 | return new ExportsHelper(fn.prototype); 109 | } 110 | 111 | /** 112 | * Exports a Function with the provided options. 113 | */ 114 | ExportsHelper.prototype.addMethod = method; 115 | ExportsHelper.prototype.method = method; 116 | function method(fn, options) { 117 | const self = this; 118 | const path = options.path || options.name || fn.name || null; 119 | const accepts = options.accepts || null; 120 | const returns = options.returns || null; 121 | const errors = options.errors || null; 122 | 123 | if (!path) { 124 | // TODO: Error. 125 | return self; 126 | } 127 | 128 | self.setPath(path, fn); 129 | fn.shared = true; 130 | fn.accepts = accepts; 131 | fn.returns = returns; 132 | fn.errors = errors; 133 | 134 | PASSTHROUGH_OPTIONS.forEach(function(key) { 135 | if (options[key]) { 136 | fn[key] = options[key]; 137 | } 138 | }); 139 | 140 | return self; 141 | } 142 | -------------------------------------------------------------------------------- /lib/looks-like-json.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | module.exports = { 9 | looksLikeJson: looksLikeJson, 10 | looksLikeJsonArray: looksLikeJsonArray, 11 | looksLikeJsonObject: looksLikeJsonObject, 12 | }; 13 | 14 | function looksLikeJson(value) { 15 | return looksLikeJsonObject(value) || looksLikeJsonArray(value); 16 | } 17 | 18 | function looksLikeJsonArray(value) { 19 | return typeof value === 'string' && 20 | value[0] === '[' && value[value.length - 1] === ']'; 21 | } 22 | 23 | function looksLikeJsonObject(value) { 24 | return typeof value === 'string' && 25 | value[0] === '{' && value[value.length - 1] === '}'; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /lib/number-checks.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // To support Node v.0.10.x 9 | const number = module.exports = { 10 | MAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER || 9007199254740991, 11 | MIN_SAFE_INTEGER: Number.MIN_SAFE_INTEGER || -9007199254740991, 12 | isInteger: Number.isInteger || function(value) { 13 | return typeof value === 'number' && 14 | isFinite(value) && 15 | Math.floor(value) === value; 16 | }, 17 | isSafeInteger: Number.isSafeInteger || function(value) { 18 | return number.isInteger(value) && 19 | value >= number.MIN_SAFE_INTEGER && 20 | value <= number.MAX_SAFE_INTEGER; 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /lib/socket-io-adapter.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const g = require('strong-globalize')(); 9 | /** 10 | * Expose `SocketIOAdapter`. 11 | */ 12 | module.exports = SocketIOAdapter; 13 | 14 | /** 15 | * Module dependencies. 16 | */ 17 | 18 | const EventEmitter = require('events').EventEmitter; 19 | const debug = require('debug')('strong-remoting:socket-io-adapter'); 20 | const util = require('util'); 21 | const inherits = util.inherits; 22 | const assert = require('assert'); 23 | const express = require('express'); 24 | const SocketIOContext = require('./socket-io-context'); 25 | 26 | /** 27 | * Create a new `RestAdapter` with the given `options`. 28 | * 29 | * @param {Object} options 30 | * @return {RestAdapter} 31 | */ 32 | 33 | function SocketIOAdapter(remotes, server) { 34 | EventEmitter.apply(this, arguments); 35 | 36 | // throw an error if args are not supplied 37 | // assert(typeof options === 'object', 38 | // 'RestAdapter requires an options object'); 39 | 40 | this.remotes = remotes; 41 | this.server = server; 42 | this.Context = SocketIOContext; 43 | } 44 | 45 | /** 46 | * Inherit from `EventEmitter`. 47 | */ 48 | 49 | inherits(SocketIOAdapter, EventEmitter); 50 | 51 | /*! 52 | * Simplified APIs 53 | */ 54 | 55 | SocketIOAdapter.create = function(remotes) { 56 | // add simplified construction / sugar here 57 | return new SocketIOAdapter(remotes); 58 | }; 59 | 60 | SocketIOAdapter.prototype.createHandler = function() { 61 | const adapter = this; 62 | const remotes = this.remotes; 63 | const Context = this.Context; 64 | const classes = this.remotes.classes(); 65 | const io = require('socket.io').listen(this.server); 66 | 67 | io.sockets.on('connection', function(socket) { 68 | socket.on('invoke', function(methodString, ctorArgs, args, id) { 69 | const method = remotes.findMethod(methodString); 70 | 71 | if (method) { 72 | // create context NEED ARGS 73 | const ctx = new Context(socket.request, ctorArgs, args); 74 | 75 | adapter.invoke(ctx, method, args, function(err, result) { 76 | socket.emit('result', { 77 | data: result, 78 | id: id, 79 | methodString: methodString, 80 | __types__: method.returns, 81 | }); 82 | }); 83 | } else { 84 | socket.emit('result', { 85 | err: g.f('method does not exist'), 86 | id: id, 87 | methodString: methodString, 88 | }); 89 | } 90 | }); 91 | }); 92 | }; 93 | 94 | SocketIOAdapter.prototype.invoke = function(ctx, method, args, callback) { 95 | const remotes = this.remotes; 96 | 97 | if (method.isStatic) { 98 | remotes.execHooks('before', method, method.ctor, ctx, function(err) { 99 | if (err) return callback(err); 100 | 101 | // invoke the static method on the actual constructor 102 | ctx.invoke(method.ctor, method, function(err, result) { 103 | if (err) return callback(err); 104 | ctx.result = result; 105 | remotes.execHooks('after', method, method.ctor, ctx, function(err) { 106 | // send the result 107 | callback(err, ctx.result); 108 | }); 109 | }); 110 | }); 111 | } else { 112 | // invoke the shared constructor to get an instance 113 | ctx.invoke(method, method.sharedCtor, function(err, inst) { 114 | if (err) return callback(err); 115 | remotes.execHooks('before', method, inst, ctx, function(err) { 116 | if (err) { 117 | callback(err); 118 | } else { 119 | // invoke the instance method 120 | ctx.invoke(inst, method, function(err, result) { 121 | if (err) return callback(err); 122 | 123 | ctx.result = result; 124 | remotes.execHooks('after', method, inst, ctx, function(err) { 125 | // send the result 126 | callback(err, ctx.result); 127 | }); 128 | }); 129 | } 130 | }); 131 | }); 132 | } 133 | }; 134 | -------------------------------------------------------------------------------- /lib/socket-io-context.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const g = require('strong-globalize')(); 9 | /** 10 | * Expose `SocketIOContext`. 11 | */ 12 | module.exports = SocketIOContext; 13 | 14 | /** 15 | * Module dependencies. 16 | */ 17 | 18 | const EventEmitter = require('events').EventEmitter; 19 | const debug = require('debug')('strong-remoting:socket-io-context'); 20 | const util = require('util'); 21 | const inherits = util.inherits; 22 | const assert = require('assert'); 23 | 24 | /** 25 | * Create a new `SocketIOContext` with the given `options`. 26 | * 27 | * @param {Object} options 28 | * @return {SocketIOContext} 29 | */ 30 | 31 | function SocketIOContext(req, ctorArgs, args) { 32 | this.req = req; 33 | this.ctorArgs = ctorArgs; 34 | this.args = args; 35 | } 36 | 37 | /** 38 | * Inherit from `EventEmitter`. 39 | */ 40 | 41 | inherits(SocketIOContext, EventEmitter); 42 | 43 | /** 44 | * Get an arg by name using the given options. 45 | * 46 | * @param {String} name 47 | * @param {Object} options **optional** 48 | */ 49 | 50 | SocketIOContext.prototype.getArgByName = function(name, options) { 51 | return this.args[name]; 52 | }; 53 | 54 | /** 55 | * Set an arg by name using the given options. 56 | * 57 | * @param {String} name 58 | * @param {Object} options **optional** 59 | */ 60 | 61 | SocketIOContext.prototype.setArgByName = function(name, options) { 62 | throw 'not implemented'; 63 | }; 64 | 65 | /** 66 | * Set part or all of the result by name using the given options. 67 | * 68 | * @param {String} name 69 | * @param {Object} options **optional** 70 | */ 71 | 72 | SocketIOContext.prototype.setResultByName = function(name, options) { 73 | 74 | }; 75 | 76 | /** 77 | * Get part or all of the result by name using the given options. 78 | * 79 | * @param {String} name 80 | * @param {Object} options **optional** 81 | */ 82 | 83 | SocketIOContext.prototype.getResultByName = function(name, options) { 84 | 85 | }; 86 | 87 | /** 88 | * Invoke the given shared method using the provided scope against 89 | * the current context. 90 | */ 91 | 92 | SocketIOContext.prototype.invoke = function(scope, method, fn) { 93 | const args = method.isSharedCtor ? this.ctorArgs : this.args; 94 | const accepts = method.accepts; 95 | const returns = method.returns; 96 | const errors = method.errors; 97 | let result; 98 | 99 | // invoke the shared method 100 | method.invoke(scope, args, function(err) { 101 | const resultArgs = arguments; 102 | 103 | if (method.name === 'on' && method.ctor instanceof EventEmitter) { 104 | resultArgs[1] = resultArgs[0]; 105 | err = null; 106 | } 107 | 108 | if (err) { 109 | return fn(err); 110 | } 111 | 112 | // map the arguments using the returns description 113 | if (returns.length > 1) { 114 | // multiple 115 | result = {}; 116 | 117 | returns.forEach(function(o, i) { 118 | // map the name of the arg in the returns desc 119 | // to the same arg in the callback 120 | result[o.name || o.arg] = resultArgs[i + 1]; 121 | }); 122 | } else { 123 | // single or no result... 124 | result = resultArgs[1]; 125 | } 126 | 127 | fn(null, result); 128 | }); 129 | }; 130 | -------------------------------------------------------------------------------- /lib/types/any.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | const numberChecks = require('../number-checks'); 11 | const looksLikeJson = require('../looks-like-json').looksLikeJson; 12 | 13 | const MAX_SAFE_INTEGER = numberChecks.MAX_SAFE_INTEGER; 14 | const MIN_SAFE_INTEGER = numberChecks.MIN_SAFE_INTEGER; 15 | 16 | const IS_INT_REGEX = /^\-?(?:[0-9]|[1-9][0-9]*)$/; 17 | const IS_FLOAT_REGEX = /^\-?([0-9]+)?\.[0-9]+$/; 18 | 19 | module.exports = { 20 | fromTypedValue: function(ctx, value, options) { 21 | return {value: value}; 22 | }, 23 | 24 | fromSloppyValue: function(ctx, value, options) { 25 | if (value === 'null' || value === null) 26 | return {value: null}; 27 | 28 | if (typeof value !== 'string') 29 | return {value: value}; 30 | 31 | if (value === '') { 32 | // Pass on empty string as undefined. 33 | // undefined was chosen so that it plays well with ES6 default parameters. 34 | return {value: undefined}; 35 | } 36 | 37 | if (value.toLowerCase() === 'true') 38 | return {value: true}; 39 | 40 | if (value.toLowerCase() === 'false') 41 | return {value: false}; 42 | 43 | if (IS_FLOAT_REGEX.test(value) || IS_INT_REGEX.test(value)) { 44 | const num = +value; 45 | // Cap at MAX_SAFE_INTEGER so we don't lose precision. 46 | if (MIN_SAFE_INTEGER <= num && num <= MAX_SAFE_INTEGER) 47 | value = num; 48 | } 49 | 50 | if (looksLikeJson(value)) { 51 | try { 52 | const result = JSON.parse(value); 53 | debug('parsed %j as JSON: %j', value, result); 54 | return this.fromTypedValue(ctx, result, options); 55 | } catch (ex) { 56 | debug('Cannot parse "any" value %j, assuming string. %s', value, ex); 57 | // no-op, use the original string value 58 | } 59 | } 60 | 61 | return this.fromTypedValue(ctx, value, options); 62 | }, 63 | 64 | validate: function(ctx, value, options) { 65 | // no-op, all values are valid 66 | }, 67 | }; 68 | -------------------------------------------------------------------------------- /lib/types/boolean.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | 11 | module.exports = { 12 | fromTypedValue: function(ctx, value, options) { 13 | const error = this.validate(ctx, value, options); 14 | return error ? {error: error} : {value: value}; 15 | }, 16 | 17 | fromSloppyValue: function(ctx, value, options) { 18 | if (value === '' || value === undefined) 19 | return {value: undefined}; 20 | 21 | if (typeof value === 'string') { 22 | switch (value.toLowerCase()) { 23 | case 'false': 24 | case '0': 25 | return {value: false}; 26 | case 'true': 27 | case '1': 28 | return {value: true}; 29 | } 30 | } 31 | return {error: invalidBooleanError()}; 32 | }, 33 | 34 | validate: function(ctx, value, options) { 35 | if (value === undefined || typeof value === 'boolean') 36 | return null; 37 | 38 | return invalidBooleanError(); 39 | }, 40 | }; 41 | 42 | function invalidBooleanError() { 43 | const err = new Error(g.f('Value is not a boolean.')); 44 | err.statusCode = 400; 45 | return err; 46 | } 47 | -------------------------------------------------------------------------------- /lib/types/date.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | 11 | module.exports = { 12 | fromTypedValue: function(ctx, value, options) { 13 | if (value === undefined) 14 | return {value: value}; 15 | 16 | if (value === null) 17 | return {error: invalidDateError()}; 18 | 19 | if (typeof value !== 'number' && typeof value !== 'string') 20 | return {error: invalidDateError()}; 21 | 22 | const result = new Date(value); 23 | const error = this.validate(result); 24 | return error ? {error: error} : {value: result}; 25 | }, 26 | 27 | fromSloppyValue: function(ctx, value, options) { 28 | if (value === '') 29 | return {value: undefined}; 30 | 31 | if (/^-?[0-9]+$/.test(value)) { 32 | // convert a timestamp string to a number 33 | // that way ?from=0 produces 1970-01-01T00:00:00.000Z 34 | value = +value; 35 | } 36 | 37 | return this.fromTypedValue(ctx, value, options); 38 | }, 39 | 40 | validate: function(ctx, value, options) { 41 | if (value === undefined) 42 | return null; 43 | 44 | if (value instanceof Date && !Number.isNaN(value.getTime())) 45 | return null; 46 | 47 | return invalidDateError(); 48 | }, 49 | }; 50 | 51 | function invalidDateError() { 52 | const err = new Error(g.f('Value is not a valid date.')); 53 | err.statusCode = 400; 54 | return err; 55 | } 56 | -------------------------------------------------------------------------------- /lib/types/geopoint.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | const GeoPoint = require('loopback-datatype-geopoint'); 11 | const looksLikeJson = require('../looks-like-json').looksLikeJson; 12 | 13 | module.exports = { 14 | fromTypedValue: function(ctx, value, options) { 15 | if (value === undefined) 16 | return {value: value}; 17 | const error = this.validate(ctx, value, options); 18 | return error ? {error: error} : {value: new GeoPoint(value)}; 19 | }, 20 | 21 | fromSloppyValue: function(ctx, value, options) { 22 | if (value === undefined || value === '') { 23 | // undefined was chosen so that it plays well with ES6 default parameters. 24 | return {value: undefined}; 25 | } 26 | 27 | if (value === null || value === 'null') 28 | return {value: null}; 29 | 30 | if (looksLikeJson(value)) { 31 | const result = parseJson(value); 32 | if (result instanceof Error) return {error: result}; 33 | return this.fromTypedValue(ctx, result, options); 34 | } 35 | 36 | // coerce input from 'arg[lat]=2.5&arg[lng]=2' or 'arg[0]=2.5&arg[1]=2 37 | if (typeof value === 'object') { 38 | const result = coerceArrayOrObject(value); 39 | return this.fromTypedValue(ctx, result, options); 40 | } 41 | return this.fromTypedValue(ctx, value, options); 42 | }, 43 | 44 | validate: function(ctx, value, options) { 45 | if (value === undefined) 46 | return null; 47 | try { 48 | new GeoPoint(value); 49 | return null; 50 | } catch (e) { 51 | const err = new Error(e.message); 52 | err.statusCode = 400; 53 | return err; 54 | } 55 | }, 56 | }; 57 | 58 | function parseJson(value) { 59 | try { 60 | const result = JSON.parse(value); 61 | debug('parsed %j as JSON: %j', value, result); 62 | return result; 63 | } catch (ex) { 64 | debug('Cannot parse object value %j. %s', value, ex); 65 | const err = new Error(g.f('Cannot parse JSON-encoded object value.')); 66 | err.statusCode = 400; 67 | return err; 68 | } 69 | } 70 | 71 | function coerceArrayOrObject(value) { 72 | const result = {}; 73 | 74 | if (Array.isArray(value)) { 75 | return value.map(Number); 76 | } 77 | 78 | for (const key in value) { 79 | const val = value[key]; 80 | result[key] = Number(val); 81 | } 82 | return result; 83 | } 84 | -------------------------------------------------------------------------------- /lib/types/integer.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | const isSafeInteger = require('../number-checks').isSafeInteger; 11 | const numberConverter = require('./number'); 12 | 13 | module.exports = { 14 | fromTypedValue: function(ctx, value, options) { 15 | const error = this.validate(ctx, value, options); 16 | return error ? {error: error} : {value: value}; 17 | }, 18 | 19 | fromSloppyValue: function(ctx, value, options) { 20 | const result = numberConverter.fromSloppyValue(ctx, value); 21 | if (result.error) 22 | return result; 23 | return this.fromTypedValue(ctx, result.value); 24 | }, 25 | 26 | validate: function(ctx, value, options) { 27 | if (value === undefined) 28 | return null; 29 | 30 | let err = numberConverter.validate(ctx, value, options); 31 | if (err) 32 | return err; 33 | 34 | if (isSafeInteger(value)) 35 | return null; 36 | 37 | err = new Error(g.f('Value is not a safe integer.')); 38 | err.statusCode = 400; 39 | return err; 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /lib/types/number.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | 11 | module.exports = { 12 | fromTypedValue: function(ctx, value, options) { 13 | const error = this.validate(ctx, value, options); 14 | return error ? {error: error} : {value: value}; 15 | }, 16 | 17 | fromSloppyValue: function(ctx, value, options) { 18 | if (value === undefined || value === '') 19 | return {value: undefined}; 20 | 21 | const result = +value; 22 | return this.fromTypedValue(ctx, result, options); 23 | }, 24 | 25 | validate: function(ctx, value, options) { 26 | if (value === undefined) 27 | return null; 28 | 29 | if (typeof value === 'number' && !isNaN(value)) 30 | return null; 31 | 32 | const err = Error(g.f('Value is not a number.')); 33 | err.statusCode = 400; 34 | return err; 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /lib/types/object.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | const looksLikeJsonObject = require('../looks-like-json').looksLikeJsonObject; 11 | 12 | module.exports = { 13 | fromTypedValue: function(ctx, value, options) { 14 | const error = this.validate(ctx, value, options); 15 | return error ? {error: error} : {value: value}; 16 | }, 17 | 18 | fromSloppyValue: function(ctx, value, options) { 19 | if (value === undefined || value === '') { 20 | // undefined was chosen so that it plays well with ES6 default parameters. 21 | return {value: undefined}; 22 | } 23 | 24 | if (value === null || value === 'null') 25 | return {value: null}; 26 | 27 | if (looksLikeJsonObject(value)) { 28 | try { 29 | const result = JSON.parse(value); 30 | debug('parsed %j as JSON: %j', value, result); 31 | return this.fromTypedValue(ctx, result, options); 32 | } catch (ex) { 33 | debug('Cannot parse object value %j. %s', value, ex); 34 | const err = new Error(g.f('Cannot parse JSON-encoded object value.')); 35 | err.statusCode = 400; 36 | return {error: err}; 37 | } 38 | } 39 | 40 | // NOTE: nested values in objects are intentionally not coerced 41 | return this.fromTypedValue(ctx, value, options); 42 | }, 43 | 44 | validate: function(ctx, value, options) { 45 | const self = this; 46 | options = options || {}; 47 | if (value === undefined || value === null) 48 | return null; 49 | 50 | if (typeof value !== 'object') 51 | return errorNotAnObject(); 52 | 53 | // reject object-like values that have their own strong-remoting type 54 | 55 | if (Array.isArray(value)) { 56 | // TODO: @davidcheung, remove this flag and support [array or Object] 57 | // see strong-remoting/issues/360 for details 58 | // allowArray flag is to handle persistedModels uses 59 | // array of Objects to batch create, which was supported in 2.x 60 | if (!options.allowArray) { 61 | return errorNotAnObject(); 62 | } else { 63 | const hasInvalidItems = value.some(function(item) { 64 | // option is not passed here so it should always reject array `item(s)` 65 | return self.validate(ctx, item); 66 | }); 67 | 68 | return hasInvalidItems ? errorArrayItemsNotAnObject() : null; 69 | } 70 | } 71 | 72 | if (value instanceof Date) 73 | return errorNotAnObject(); 74 | 75 | return null; 76 | }, 77 | }; 78 | 79 | function errorNotAnObject() { 80 | const err = new Error(g.f('Value is not an object.')); 81 | err.statusCode = 400; 82 | return err; 83 | } 84 | 85 | function errorArrayItemsNotAnObject() { 86 | const err = new Error(g.f('Some of array items are not an object.')); 87 | err.statusCode = 400; 88 | return err; 89 | } 90 | -------------------------------------------------------------------------------- /lib/types/string.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('strong-remoting:http-coercion'); 9 | const g = require('strong-globalize')(); 10 | 11 | module.exports = { 12 | fromTypedValue: function(ctx, value, options) { 13 | const error = this.validate(ctx, value, options); 14 | return error ? {error: error} : {value: value}; 15 | }, 16 | 17 | fromSloppyValue: function(ctx, value, options) { 18 | if (value === '') { 19 | // Pass on empty string as undefined. 20 | // undefined was chosen so that it plays well with ES6 default parameters. 21 | return {value: undefined}; 22 | } 23 | 24 | // TODO(bajtos) should we reject objects/arrays values created from complex 25 | // query strings, e.g. ?arg[1]=a&arg[2]=b 26 | if (value !== undefined && value !== null) 27 | value = '' + value; 28 | 29 | return this.fromTypedValue(ctx, value, options); 30 | }, 31 | 32 | validate: function(ctx, value, options) { 33 | if (value === undefined || typeof value === 'string') 34 | return null; 35 | 36 | const err = new Error(g.f('Value is not a string.')); 37 | err.statusCode = 400; 38 | return err; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strong-remoting", 3 | "description": "StrongLoop Remoting Module", 4 | "keywords": [ 5 | "StrongLoop", 6 | "LoopBack", 7 | "Remoting", 8 | "REST" 9 | ], 10 | "author": "IBM Corp.", 11 | "version": "3.17.0", 12 | "scripts": { 13 | "coverage": "nyc report --reporter=text-lcov | coveralls", 14 | "lint": "eslint .", 15 | "lint:fix": "eslint . --fix", 16 | "test": "nyc mocha", 17 | "posttest": "npm run lint" 18 | }, 19 | "engines": { 20 | "node": ">=8" 21 | }, 22 | "dependencies": { 23 | "async": "^3.1.0", 24 | "body-parser": "^1.12.4", 25 | "debug": "^4.1.1", 26 | "depd": "^2.0.0", 27 | "escape-string-regexp": "^2.0.0", 28 | "eventemitter2": "^5.0.1", 29 | "express": "4.x", 30 | "inflection": "^1.7.1", 31 | "jayson": "^2.0.5", 32 | "js2xmlparser": "^3.0.0", 33 | "loopback-datatype-geopoint": "^1.0.0", 34 | "loopback-phase": "^3.1.0", 35 | "mux-demux": "^3.7.9", 36 | "qs": "^6.2.1", 37 | "request": "^2.83.0", 38 | "sse": "0.0.8", 39 | "strong-error-handler": "^3.0.0", 40 | "strong-globalize": "^5.0.2", 41 | "traverse": "^0.6.6", 42 | "xml2js": "^0.4.8" 43 | }, 44 | "devDependencies": { 45 | "bluebird": "^3.4.1", 46 | "chai": "^4.1.2", 47 | "coveralls": "^3.0.1", 48 | "dirty-chai": "^2.0.1", 49 | "eslint": "^6.5.1", 50 | "eslint-config-loopback": "^13.1.0", 51 | "event-stream": "^4.0.1", 52 | "eventsource": "^1.0.5", 53 | "http-auth": "^3.0.1", 54 | "mocha": "^6.2.1", 55 | "nyc": "^14.1.1", 56 | "requirejs": "^2.2.0", 57 | "socket.io": "^2.1.1", 58 | "supertest": "^4.0.2" 59 | }, 60 | "repository": { 61 | "type": "git", 62 | "url": "https://github.com/strongloop/strong-remoting" 63 | }, 64 | "browser": { 65 | "express": false, 66 | "body-parser": false, 67 | "js2xmlparser": false, 68 | "strong-error-handler": false 69 | }, 70 | "license": "Artistic-2.0" 71 | } 72 | -------------------------------------------------------------------------------- /test/authorize-hook.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2018. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const expect = require('./helpers/expect'); 9 | const express = require('express'); 10 | const RemoteObjects = require('../'); 11 | const User = require('./e2e/fixtures/user'); 12 | const fmt = require('util').format; 13 | 14 | describe('authorization hook', function() { 15 | let server, remotes; 16 | 17 | before(function setupServer(done) { 18 | const app = express(); 19 | remotes = RemoteObjects.create(); 20 | remotes.exports.User = User; 21 | app.use(remotes.handler('rest')); 22 | server = app.listen(0, '127.0.0.1', done); 23 | }); 24 | 25 | after(function teardownServer(done) { 26 | server.close(done); 27 | }); 28 | 29 | describe('given a remotes object with an authorization hook', function() { 30 | it('should be called when a remote method is invoked', function(done) { 31 | const callStack = []; 32 | remotes.authorization = function(ctx, next) { 33 | callStack.push('authorization'); 34 | next(); 35 | }; 36 | 37 | remotes.before('User.login', function(ctx, next) { 38 | callStack.push('before'); 39 | next(); 40 | }); 41 | 42 | invokeRemote(server.address().port, 43 | function(err, session) { 44 | expect(err).to.not.exist(); 45 | expect(session.userId).to.equal(123); 46 | // vvvvvvvv - local before hook 47 | expect(callStack).to.eql(['before', 'authorization', 'before']); 48 | done(); 49 | }); 50 | }); 51 | }); 52 | 53 | function invokeRemote(port, callback) { 54 | const url = 'http://127.0.0.1:' + port; 55 | const method = 'User.login'; 56 | const args = [{username: 'joe', password: 'secret'}]; 57 | 58 | remotes.connect(url, 'rest'); 59 | remotes.invoke(method, args, callback); 60 | } 61 | }); 62 | -------------------------------------------------------------------------------- /test/data/foo.json: -------------------------------------------------------------------------------- 1 | {"bar":"baz"} 2 | -------------------------------------------------------------------------------- /test/e2e/e2e-server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | // this server should be started before running tests in the e2e directory 9 | const path = require('path'); 10 | const FIXTURES = path.join(__dirname, 'fixtures'); 11 | const remotes = require(path.join(FIXTURES, 'remotes')); 12 | 13 | require('http') 14 | .createServer(remotes.handler('rest')) 15 | .listen(3000, function() { 16 | console.log('e2e server listening...'); 17 | }); 18 | -------------------------------------------------------------------------------- /test/e2e/fixtures/remotes.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const RemoteObjects = require('../../../'); 9 | const remotes = module.exports = RemoteObjects.create(); 10 | 11 | remotes.exports.User = require('./user'); 12 | -------------------------------------------------------------------------------- /test/e2e/fixtures/user.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('test user'); 9 | 10 | module.exports = User; 11 | 12 | function User() { 13 | debug('constructed a user: %j', this); 14 | } 15 | 16 | User.sharedCtor = function(id, callback) { 17 | const user = new User(); 18 | user.username = 'joe'; 19 | callback(null, user); 20 | }; 21 | User.sharedCtor.shared = true; 22 | User.sharedCtor.accepts = {arg: 'id', type: 'string'}; 23 | User.sharedCtor.http = [ 24 | {path: '/:id', verb: 'get'}, 25 | {path: '/', verb: 'get'}, 26 | ]; 27 | 28 | const login = User.login = function(credentials, callback) { 29 | debug('login with credentials: %j', credentials); 30 | setTimeout(function() { 31 | if (!credentials.password) { 32 | return callback(new Error('password required')); 33 | } 34 | callback(null, {userId: 123}); 35 | }, 0); 36 | }; 37 | login.shared = true; 38 | login.accepts = {arg: 'credentials', type: 'object'}; 39 | login.returns = {arg: 'session', type: 'object'}; 40 | 41 | const hasUsername = User.prototype.hasUsername = function(username, callback) { 42 | callback(null, username === this.username); 43 | }; 44 | 45 | hasUsername.shared = true; 46 | hasUsername.accepts = {arg: 'username', type: 'string'}; 47 | hasUsername.returns = {arg: 'hasUsername', type: 'boolean'}; 48 | -------------------------------------------------------------------------------- /test/e2e/smoke.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const RemoteObjects = require('../../'); 9 | const expect = require('chai').expect; 10 | const REMOTE_URL = 'http://localhost:3000'; 11 | const remotes = require('./fixtures/remotes'); 12 | 13 | remotes.connect(REMOTE_URL, 'rest'); 14 | 15 | describe('smoke test', function() { 16 | describe('remote.invoke()', function() { 17 | it('invokes a remote static method', function(done) { 18 | remotes.invoke( 19 | 'User.login', 20 | [{username: 'joe', password: 'secret'}], 21 | function(err, session) { 22 | expect(err).to.not.exist(); 23 | expect(session.userId).to.equal(123); 24 | done(); 25 | }, 26 | ); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/helpers/expect.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const chai = require('chai'); 9 | chai.use(require('dirty-chai')); 10 | 11 | module.exports = chai.expect; 12 | -------------------------------------------------------------------------------- /test/helpers/shared-objects-factory.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /* Copyright (c) 2013 StrongLoop, Inc. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | 'use strict'; 28 | 29 | const extend = require('util')._extend; 30 | 31 | exports.createSharedClass = function createSharedClass(config) { 32 | // create a class that can be remoted 33 | const SharedClass = function(id) { 34 | this.id = id; 35 | }; 36 | extend(SharedClass, config); 37 | 38 | SharedClass.shared = true; 39 | 40 | SharedClass.sharedCtor = function(id, cb) { 41 | // allow tests to override the implementation of shared ctor 42 | // while preserving remoting metadata 43 | this._sharedCtor(id, cb); 44 | }; 45 | 46 | extend(SharedClass.sharedCtor, { 47 | shared: true, 48 | accepts: [{arg: 'id', type: 'any', http: {source: 'path'}}], 49 | http: {path: '/:id'}, 50 | returns: {arg: 'instance', type: 'object', root: true}, 51 | }); 52 | 53 | SharedClass._sharedCtor = function(id, cb) { 54 | cb(null, new SharedClass(id)); 55 | }; 56 | 57 | return SharedClass; 58 | }; 59 | -------------------------------------------------------------------------------- /test/helpers/test-server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | module.exports = function(callback) { 9 | 10 | }; 11 | -------------------------------------------------------------------------------- /test/jsonrpc.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2013,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const assert = require('assert'); 9 | const RemoteObjects = require('../'); 10 | const express = require('express'); 11 | const request = require('supertest'); 12 | const SharedClass = require('../lib/shared-class'); 13 | 14 | describe('strong-remoting-jsonrpc', function() { 15 | let app, server, objects, remotes; 16 | 17 | // setup 18 | beforeEach(function() { 19 | if (server) server.close(); 20 | objects = RemoteObjects.create({json: {limit: '1kb'}}); 21 | remotes = objects.exports; 22 | app = express(); 23 | }); 24 | 25 | function jsonrpc(url, method, parameters) { 26 | return request(app).post(url) 27 | .set('Accept', 'application/json') 28 | .set('Content-Type', 'application/json') 29 | .send({'jsonrpc': '2.0', 'method': method, 'params': parameters, 'id': 1}) 30 | .expect('Content-Type', /json/); 31 | } 32 | 33 | describe('handlers', function() { 34 | describe('jsonrpc', function() { 35 | beforeEach(function() { 36 | app.use(function(req, res, next) { 37 | // create the handler for each request 38 | objects.handler('jsonrpc').apply(objects, arguments); 39 | }); 40 | function greet(msg, fn) { 41 | fn(null, msg); 42 | } 43 | greet.accepts = [ 44 | {'arg': 'msg', 'type': 'string'}, 45 | ]; 46 | 47 | // Create a shared method directly on the function object 48 | remotes.user = { 49 | greet: greet, 50 | }; 51 | greet.shared = true; 52 | 53 | // Create a shared method directly on the function object for named parameters tests 54 | function sum(numA, numB, cb) { 55 | cb(null, numA + numB); 56 | } 57 | remotes.mathematic = { 58 | sum: sum, 59 | }; 60 | sum.accepts = [ 61 | {'arg': 'numA', 'type': 'number'}, 62 | {'arg': 'numB', 'type': 'number'}, 63 | ]; 64 | sum.returns = { 65 | 'arg': 'sum', 66 | 'type': 'number', 67 | }; 68 | sum.shared = true; 69 | 70 | // Create a shared method using SharedClass/SharedMethod 71 | function Product() { 72 | } 73 | 74 | Product.getPrice = function(cb) { 75 | process.nextTick(function() { 76 | return cb(null, 100); 77 | }); 78 | }; 79 | 80 | const productClass = new SharedClass('product', Product); 81 | productClass.defineMethod('getPrice', {isStatic: true}); 82 | objects.addClass(productClass); 83 | }); 84 | 85 | it('should support calling object methods', function(done) { 86 | jsonrpc('/user/jsonrpc', 'greet', ['JS']) 87 | .expect({'jsonrpc': '2.0', 'id': 1, 'result': 'JS'}, done); 88 | }); 89 | it('Should successfully call a method with named parameters', function(done) { 90 | jsonrpc('/mathematic/jsonrpc', 'sum', {'numB': 9, 'numA': 2}) 91 | .expect({'jsonrpc': '2.0', 'id': 1, 'result': 11}, done); 92 | }); 93 | it('should support a remote method using shared method', function(done) { 94 | jsonrpc('/product/jsonrpc', 'getPrice', []) 95 | .expect({'jsonrpc': '2.0', 'id': 1, 'result': 100}, done); 96 | }); 97 | 98 | it('should report error for non-existent methods', function(done) { 99 | jsonrpc('/user/jsonrpc', 'greet1', ['JS']) 100 | .expect({ 101 | 'jsonrpc': '2.0', 102 | 'id': 1, 103 | 'error': { 104 | 'code': -32601, 105 | 'message': 'Method not found', 106 | }, 107 | }, done); 108 | }); 109 | 110 | // The 1kb limit is set by RemoteObjects.create({json: {limit: '1kb'}}); 111 | it('should reject json payload larger than 1kb', function(done) { 112 | // Build an object that is larger than 1kb 113 | let name = ''; 114 | for (let i = 0; i < 2048; i++) { 115 | name += '11111111111'; 116 | } 117 | 118 | jsonrpc('/user/jsonrpc', 'greet', [name]) 119 | .expect(413, done); 120 | }); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | // Karma configuration 7 | // http://karma-runner.github.io/0.12/config/configuration-file.html 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(config) { 12 | config.set({ 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '../', 15 | 16 | // frameworks to use 17 | frameworks: ['mocha', 'browserify'], 18 | 19 | plugins: [ 20 | 'karma-browserify', 21 | 'karma-mocha', 22 | 'karma-phantomjs-launcher', 23 | 'karma-chrome-launcher', 24 | 'karma-junit-reporter', 25 | ], 26 | 27 | // list of files / patterns to load in the browser 28 | files: [ 29 | 'test/e2e/fixtures/*.js', 30 | 'test/e2e/smoke.test.js', 31 | ], 32 | 33 | // list of files to exclude 34 | exclude: [ 35 | 36 | ], 37 | 38 | // test results reporter to use 39 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 40 | reporters: ['dots'], 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | // cli runner port 46 | runnerPort: 9100, 47 | 48 | // enable / disable colors in the output (reporters and logs) 49 | colors: true, 50 | 51 | // level of logging 52 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || 53 | // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 54 | logLevel: 'warn', 55 | 56 | // enable / disable watching file and executing tests 57 | // whenever any file changes 58 | autoWatch: true, 59 | 60 | // Start these browsers, currently available: 61 | // - Chrome 62 | // - ChromeCanary 63 | // - Firefox 64 | // - Opera 65 | // - Safari (only Mac) 66 | // - PhantomJS 67 | // - IE (only Windows) 68 | browsers: [ 69 | 'Chrome', 70 | ], 71 | 72 | // If browser does not capture in given timeout [ms], kill it 73 | captureTimeout: 60000, 74 | 75 | // Continuous Integration mode 76 | // if true, it capture browsers, run tests and exit 77 | singleRun: false, 78 | 79 | // Browserify config (all optional) 80 | browserify: { 81 | // extensions: ['.coffee'], 82 | ignore: [ 83 | 'superagent', 84 | 'supertest', 85 | ], 86 | // transform: ['coffeeify'], 87 | debug: true, 88 | // noParse: ['jquery'], 89 | watch: true, 90 | }, 91 | 92 | // Add browserify to preprocessors 93 | preprocessors: { 94 | 'test/e2e/**': ['browserify'], 95 | // 'lib/*.js': ['browserify'] 96 | }, 97 | }); 98 | }; 99 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter dot 2 | --exit 3 | -------------------------------------------------------------------------------- /test/phase-handlers.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const expect = require('chai').expect; 9 | const express = require('express'); 10 | const RemoteObjects = require('../'); 11 | const User = require('./e2e/fixtures/user'); 12 | 13 | describe('phase handlers', function() { 14 | let server, remotes, clientRemotes; 15 | 16 | beforeEach(function setupServer(done) { 17 | const app = express(); 18 | remotes = RemoteObjects.create(); 19 | remotes.exports.User = User; 20 | app.use(function(req, res, next) { 21 | // always build a new handler to pick new methods added by tests 22 | remotes.handler('rest')(req, res, next); 23 | }); 24 | server = app.listen(0, '127.0.0.1', done); 25 | }); 26 | 27 | beforeEach(function setupClient() { 28 | clientRemotes = RemoteObjects.create(); 29 | clientRemotes.exports.User = User; 30 | const url = 'http://127.0.0.1:' + server.address().port; 31 | clientRemotes.connect(url, 'rest'); 32 | }); 33 | 34 | afterEach(function teardownServer(done) { 35 | server.close(done); 36 | }); 37 | 38 | it('has built-in phases "auth" and "invoke"', function() { 39 | expect(remotes.phases.getPhaseNames()).to.eql(['auth', 'invoke']); 40 | }); 41 | 42 | it('invokes phases in the correct order', function(done) { 43 | const phasesRun = []; 44 | const pushNameAndNext = function(name) { 45 | return function(ctx, next) { phasesRun.push(name); next(); }; 46 | }; 47 | 48 | remotes.phases.find('auth').use(pushNameAndNext('phaseHandler-auth')); 49 | const invokePhase = remotes.phases.find('invoke'); 50 | invokePhase.before(pushNameAndNext('phaseHandler-invoke:before')); 51 | invokePhase.use(pushNameAndNext('phaseHandler-invoke:use')); 52 | invokePhase.after(pushNameAndNext('phaseHandler-invoke:after')); 53 | 54 | remotes.authorization = pushNameAndNext('hook-authorization'); 55 | remotes.before('**', pushNameAndNext('hook-remotes.before')); 56 | remotes.after('**', pushNameAndNext('hook-remotes.after')); 57 | 58 | User.pushName = function(cb) { phasesRun.push('invoke method'); cb(); }; 59 | User.pushName.shared = true; 60 | 61 | invokeRemote('User.pushName', function(err) { 62 | if (err) return done(err); 63 | expect(phasesRun).to.eql([ 64 | 'hook-authorization', 65 | 'phaseHandler-auth', 66 | 'hook-remotes.before', 67 | 'phaseHandler-invoke:before', 68 | 'invoke method', 69 | 'phaseHandler-invoke:use', 70 | 'hook-remotes.after', 71 | 'phaseHandler-invoke:after', 72 | ]); 73 | done(); 74 | }); 75 | }); 76 | 77 | describe('registerPhaseHandler', function() { 78 | let handlersRun; 79 | 80 | beforeEach(function() { 81 | User.static = function(cb) { cb(); }; 82 | User.static.shared = true; 83 | 84 | User.prototype.proto = function(cb) { cb(); }; 85 | User.prototype.proto.shared = true; 86 | 87 | handlersRun = []; 88 | function register(wildcard) { 89 | remotes.registerPhaseHandler('invoke', wildcard, function(ctx, next) { 90 | handlersRun.push(wildcard); 91 | next(); 92 | }); 93 | } 94 | 95 | register('**'); 96 | register('User.**'); 97 | 98 | register('User.*'); 99 | register('User.static'); 100 | register('User.proto'); // does not exist 101 | 102 | register('User.prototype.*'); 103 | register('User.prototype.proto'); 104 | register('User.prototype.static'); // does not exist 105 | }); 106 | 107 | it('matches static methods using wildcards', function(done) { 108 | invokeRemote('User.static', function(err) { 109 | if (err) return done(err); 110 | expect(handlersRun).to.eql([ 111 | '**', 112 | 'User.**', 113 | 'User.*', 114 | 'User.static', 115 | ]); 116 | done(); 117 | }); 118 | }); 119 | 120 | it('matches prototype methods using wildcards', function(done) { 121 | invokeRemote('User.prototype.proto', function(err) { 122 | if (err) return done(err); 123 | expect(handlersRun).to.eql([ 124 | '**', 125 | 'User.**', 126 | 'User.prototype.*', 127 | 'User.prototype.proto', 128 | ]); 129 | done(); 130 | }); 131 | }); 132 | }); 133 | 134 | function invokeRemote(method, callback) { 135 | const args = []; 136 | clientRemotes.invoke(method, args, callback); 137 | } 138 | }); 139 | -------------------------------------------------------------------------------- /test/remote-objects.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2017,2018. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const expect = require('chai').expect; 9 | const RemoteObjects = require('../'); 10 | const RestAdapter = require('../lib/rest-adapter'); 11 | const SharedClass = require('../lib/shared-class'); 12 | 13 | describe('RemoteObjects', function() { 14 | let remotes; 15 | beforeEach(function() { remotes = RemoteObjects.create(); }); 16 | 17 | describe('RemoteObjects.handler()', function() { 18 | it('should throws an error if the provided adapter is not valid', function() { 19 | const invalidAdapter = function() {}; 20 | try { 21 | remotes.handler(invalidAdapter); 22 | } catch (err) { 23 | expect(err.message).to.contain('Invalid adapter class'); 24 | return; 25 | } 26 | throw new Error('should not get here'); 27 | }); 28 | 29 | it('should accept a provided adapter if valid', function() { 30 | remotes.handler(RestAdapter); 31 | }); 32 | }); 33 | 34 | describe('deleteClassByName()', () => { 35 | it('removes the class', () => { 36 | class TempClass {} 37 | 38 | const sharedClass = new SharedClass('TempClass', TempClass); 39 | remotes.addClass(sharedClass); 40 | expect(Object.keys(remotes._classes)).to.contain('TempClass'); 41 | 42 | remotes.deleteClassByName('TempClass'); 43 | expect(Object.keys(remotes._classes)).to.not.contain('TempClass'); 44 | }); 45 | 46 | it('removes the remote hooks', () => { 47 | remotes.before('TempClass.' + 'find', function(ctx, next) { next(); }); 48 | remotes.after('TempClass.' + 'find', function(ctx, next) { next(); }); 49 | remotes.afterError('TempClass.' + 'find', function(ctx, next) { next(); }); 50 | expect(Object.keys(remotes.listenerTree.before)).to.contain('TempClass'); 51 | expect(Object.keys(remotes.listenerTree.after)).to.contain('TempClass'); 52 | expect(Object.keys(remotes.listenerTree.afterError)).to.contain('TempClass'); 53 | 54 | remotes.deleteClassByName('TempClass'); 55 | expect(Object.keys(remotes.listenerTree.before)).to.not.contain('TempClass'); 56 | expect(Object.keys(remotes.listenerTree.after)).to.not.contain('TempClass'); 57 | expect(Object.keys(remotes.listenerTree.afterError)).to.not.contain('TempClass'); 58 | }); 59 | }); 60 | 61 | describe('deleteTypeByName()', () => { 62 | it('removes the type converter', () => { 63 | class MyType {} 64 | 65 | const registeredTypes = remotes._typeRegistry._types; 66 | remotes.defineObjectType('MyType', data => new MyType()); 67 | expect(Object.keys(registeredTypes)).to.contain('mytype'); 68 | 69 | remotes.deleteTypeByName('MyType'); 70 | expect(Object.keys(registeredTypes)).to.not.contain('mytype'); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/rest-coercion/README.md: -------------------------------------------------------------------------------- 1 | # Test coercion of input arguments in REST adapter 2 | 3 | The tests are grouped based on where the input argument is read from 4 | and what is the content-type of that source. 5 | 6 | After the tests finish, a CSV report is written in `report.csv`. This report 7 | makes it easy to compare results between different strong-remoting versions, 8 | just run `diff -du report1.csv report2.csv`. 9 | 10 | ## Query string 11 | 12 | Arg spec: `{ arg: 'arg', http: { source: 'query' } }` 13 | 14 | Example request: 15 | 16 | ```http 17 | GET /api?arg=value&arg2=value2 HTTP/1.1 18 | ``` 19 | 20 | See `urlencoded-*.suite.js` for tests. 21 | 22 | ## URL-encoded form 23 | 24 | Arg spec: `{ arg: 'arg', http: { source: 'form' } }` 25 | 26 | Example request: 27 | 28 | ```http 29 | POST /api HTTP/1.1 30 | Content-Type: application/x-www-form-urlencoded 31 | 32 | arg=value&arg2=value2 33 | ``` 34 | 35 | See `urlencoded-*.suite.js` for test cases. 36 | 37 | ## JSON-encoded form 38 | 39 | Arg spec: `{ arg: 'arg', http: { source: 'form' } }` 40 | 41 | Example request: 42 | 43 | ```http 44 | POST /api HTTP/1.1 45 | Content-Type: application/json 46 | 47 | { 48 | "arg": "value", 49 | "arg2": "value2" 50 | } 51 | ``` 52 | 53 | See `jsonform-*.suite.js` for test cases. 54 | 55 | ## JSON-encoded body 56 | 57 | Arg spec: `{ arg: 'arg', http: { source: 'body' } }` 58 | 59 | Example request: 60 | 61 | ```http 62 | POST /api HTTP/1.1 63 | Content-Type: application/json 64 | 65 | { 66 | "name": "Superb", 67 | "maker": "Skoda" 68 | } 69 | ``` 70 | 71 | See `jsonbody-*.suite.js` for test cases. 72 | 73 | The difference between JSON form and body is that the argument is set to full 74 | request body argument's http `source` is `body`. 75 | 76 | ## URL-encoded body 77 | 78 | We don't have coverage for this scenario yet. 79 | 80 | ## URL path parameters 81 | 82 | We don't have coverage for this scenario yet. 83 | -------------------------------------------------------------------------------- /test/rest-coercion/_custom-class.context.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const extend = require('util')._extend; 9 | 10 | function CustomClass(data) { 11 | if (!(this instanceof CustomClass)) 12 | return new CustomClass(data); 13 | 14 | if (data.invalid) { 15 | const err = new Error('Invalid CustomClass value.'); 16 | err.statusCode = 400; 17 | throw err; 18 | } 19 | 20 | if ('name' in data) 21 | this.name = data.name; 22 | else 23 | this.empty = true; 24 | } 25 | 26 | module.exports = function createCustomClassContext(ctx) { 27 | beforeEach(function registerCustomClass() { 28 | if ('customclass' in ctx.remoteObjects._typeRegistry._types) { 29 | // This happens when there are multiple instances of this beforEach hook 30 | // registered. Typically when createCustomClassContext is called 31 | // inside the top-level "describe" block. 32 | return; 33 | } 34 | ctx.remoteObjects.defineObjectType('CustomClass', CustomClass); 35 | }); 36 | 37 | return extend(Object.create(ctx), { 38 | CustomClass: CustomClass, 39 | verifyTestCases: verifyTestCases, 40 | }); 41 | 42 | function verifyTestCases(argSpec, testCases) { 43 | for (const ix in testCases) { 44 | if (testCases[ix].length === 1) { 45 | const data = testCases[ix][0]; 46 | testCases[ix] = [data, new CustomClass(data)]; 47 | } 48 | } 49 | ctx.verifyTestCases(argSpec, testCases); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /test/rest-coercion/_jsonbody.context.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('test'); 9 | const expect = require('chai').expect; 10 | const util = require('util'); 11 | const format = util.format; 12 | const extend = util._extend; 13 | 14 | module.exports = function createJsonBodyContext(ctx) { 15 | return extend(Object.create(ctx), { 16 | verifyTestCases: verifyTestCases, 17 | }); 18 | 19 | /** 20 | * Verify a set of test-cases for a given argument specification 21 | * (remoting definition). 22 | * 23 | * @param {Object} argSpec Argument definition, note that `http` 24 | * settings are injected automatically. 25 | * @param {Array} testCases List of test cases to run & verify. 26 | * A test-case is a tuple [request-body, expected-argument-value] 27 | * The second item is optional, meaning the response should be the same 28 | * as the request. 29 | * 30 | * **Example** 31 | * 32 | * ```js 33 | * verifyTestCases({ arg: 'name', type: 'any' }, [ 34 | * [{ txt: null }], 35 | * [{ num: 1}, ERROR_BAD_REQUEST] 36 | * ]); 37 | * ``` 38 | * 39 | * In this scenario, we build a shared method that accepts a single "name" 40 | * argument of "any" type. Then we run two test cases: 41 | * 42 | * The first one sends JSON request body `{ "txt": null }` and expects 43 | * the argument to be set to `{ arg: null }` (the same value). 44 | * 45 | * The second one sends JSON request body `{ "num": 1 }` and expects 46 | * the request to fail with HTTP status 400 Bad Request. 47 | */ 48 | function verifyTestCases(argSpec, testCases) { 49 | testCases.forEach(function(tc) { 50 | const requestBody = tc[0]; 51 | let expectedValue = tc[1]; 52 | if (tc.length < 2) 53 | expectedValue = requestBody; 54 | 55 | // supertests sends string data verbatim 56 | const niceInput = typeof requestBody === 'string' ? 57 | requestBody : JSON.stringify(requestBody); 58 | const niceExpectation = ctx.prettyExpectation(expectedValue); 59 | const testName = format('coerces %s to %s', niceInput, niceExpectation); 60 | 61 | it(testName, function(done) { 62 | ctx.runtime.currentInput = niceInput; 63 | testCoercion(argSpec, requestBody, expectedValue, done); 64 | }); 65 | }); 66 | } 67 | 68 | function testCoercion(argSpec, requestBody, expectedResult, done) { 69 | let argValue; 70 | const testClass = ctx.remoteObjects.exports.testClass = { 71 | testMethod: function(arg, cb) { 72 | argValue = arg; 73 | return cb(null, true); 74 | }, 75 | }; 76 | 77 | extend(testClass.testMethod, { 78 | shared: true, 79 | accepts: extend(argSpec, {http: {source: 'body'}}), 80 | returns: {name: 'success', type: 'boolean'}, 81 | }); 82 | 83 | ctx.request.get('/testClass/testMethod') 84 | .set('Accept', 'application/json') 85 | .set('Content-Type', 'application/json') 86 | .send(requestBody) 87 | .expect('Content-Type', /json/) 88 | .end(function(err, res) { 89 | ctx.verifyResultOnResponse(err, res, argValue, expectedResult, done); 90 | }); 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /test/rest-coercion/_jsonform.context.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('test'); 9 | const expect = require('chai').expect; 10 | const util = require('util'); 11 | const format = util.format; 12 | const extend = util._extend; 13 | 14 | const EMPTY_BODY = {}; 15 | 16 | module.exports = function createJsonBodyContext(ctx) { 17 | return extend(Object.create(ctx), { 18 | /** Send a request with an empty body (that is still valid JSON) */ 19 | EMPTY_BODY: EMPTY_BODY, 20 | verifyTestCases: verifyTestCases, 21 | }); 22 | 23 | /** 24 | * Verify a set of test-cases for a given argument specification 25 | * (remoting definition). 26 | * 27 | * @param {Object} argSpec Argument definition, note that `http` 28 | * settings are injected automatically. 29 | * @param {Array} testCases List of test cases to run & verify. 30 | * A test-case is a tuple [request-body, expected-argument-value] 31 | * 32 | * **Example** 33 | * 34 | * ```js 35 | * verifyTestCases({ arg: 'arg', type: 'number' }, [ 36 | * [{ arg: null }, 0], 37 | * [{ arg: 'text' }, ERROR_BAD_REQUEST] 38 | * ]); 39 | * ``` 40 | * 41 | * In this scenario, we build a shared method that accepts a single "arg" 42 | * argument of "number" type. Then we run two test cases: 43 | * 44 | * The first one sends JSON request body `{ "arg": null }` and expects 45 | * the argument to be set to number `0`. 46 | * 47 | * The second one sends JSON request body `{ "arg": 'text' }` and expects 48 | * the request to fail with HTTP status 400 Bad Request. 49 | */ 50 | function verifyTestCases(argSpec, testCases) { 51 | testCases.forEach(function(tc) { 52 | const requestBody = tc[0]; 53 | const expectedValue = tc[1]; 54 | 55 | const niceInput = requestBody === EMPTY_BODY ? 56 | 'empty body' : JSON.stringify(requestBody); 57 | const niceExpectation = ctx.prettyExpectation(expectedValue); 58 | const testName = format('coerces %s to %s', niceInput, niceExpectation); 59 | 60 | it(testName, function(done) { 61 | ctx.runtime.currentInput = niceInput; 62 | testCoercion(argSpec, requestBody, expectedValue, done); 63 | }); 64 | }); 65 | } 66 | 67 | function testCoercion(argSpec, requestBody, expectedResult, done) { 68 | let argValue; 69 | const testClass = ctx.remoteObjects.exports.testClass = { 70 | testMethod: function(arg, cb) { 71 | argValue = arg; 72 | return cb(null, true); 73 | }, 74 | }; 75 | 76 | extend(testClass.testMethod, { 77 | shared: true, 78 | accepts: extend(argSpec, {http: {source: 'form'}}), 79 | returns: {name: 'success', type: 'boolean'}, 80 | }); 81 | 82 | ctx.request.get('/testClass/testMethod') 83 | .set('Accept', 'application/json') 84 | .set('Content-Type', 'application/json') 85 | .send(requestBody) 86 | .expect('Content-Type', /json/) 87 | .end(function(err, res) { 88 | ctx.verifyResultOnResponse(err, res, argValue, expectedResult, done); 89 | }); 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /test/rest-coercion/_urlencoded.context.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const debug = require('debug')('test'); 9 | const expect = require('chai').expect; 10 | const util = require('util'); 11 | const format = util.format; 12 | const extend = util._extend; 13 | 14 | const EMPTY_QUERY = ''; 15 | 16 | module.exports = function createUrlEncodedContext(ctx, target) { 17 | const TARGET_QUERY_STRING = target === 'qs'; 18 | 19 | return extend(Object.create(ctx), { 20 | /** Send empty data, i.e. empty request body or no query string */ 21 | EMPTY_QUERY: EMPTY_QUERY, 22 | verifyTestCases: verifyTestCases, 23 | }); 24 | 25 | /** 26 | * Verify a set of test-cases for a given argument specification 27 | * (remoting definition). 28 | * 29 | * @param {Object} argSpec Argument definition, note that `http` 30 | * settings are injected automatically. 31 | * @param {Array} testCases List of test cases to run & verify. 32 | * A test-case is a tuple [request-body, expected-argument-value] 33 | * 34 | * **Example** 35 | * 36 | * ```js 37 | * verifyTestCases({ arg: 'arg', type: 'number' }, [ 38 | * [{ arg: 0 }, 0], 39 | * [{ arg: 'text' }, ERROR_BAD_REQUEST] 40 | * ]); 41 | * ``` 42 | * 43 | * In this scenario, we build a shared method that accepts a single "arg" 44 | * argument of "number" type. Then we run two test cases: 45 | * 46 | * The first one sends url-encoded data `arg=0` either in query-string 47 | * or request body (depending on `target` configuration set earlier) 48 | * and expects the argument to be set to number `0`. 49 | * 50 | * The second one sends `arg=text` and expects the request to fail 51 | * with HTTP status 400 Bad Request. 52 | */ 53 | function verifyTestCases(argSpec, testCases) { 54 | testCases.forEach(function(tc) { 55 | const queryString = tc[0]; 56 | const expectedValue = tc[1]; 57 | 58 | const niceInput = queryString === EMPTY_QUERY ? 59 | TARGET_QUERY_STRING ? 'empty query' : 'empty form' : 60 | '?' + queryString; 61 | const niceExpectation = ctx.prettyExpectation(expectedValue); 62 | const testName = format('coerces %s to %s', niceInput, niceExpectation); 63 | 64 | it(testName, function(done) { 65 | ctx.runtime.currentInput = niceInput; 66 | testCoercion(argSpec, queryString, expectedValue, done); 67 | }); 68 | }); 69 | } 70 | 71 | function testCoercion(argSpec, queryString, expectedResult, done) { 72 | let argValue; 73 | const testClass = ctx.remoteObjects.exports.testClass = { 74 | testMethod: function(arg, cb) { 75 | argValue = arg; 76 | return cb(null, true); 77 | }, 78 | }; 79 | 80 | const source = TARGET_QUERY_STRING ? 'query' : 'form'; 81 | extend(testClass.testMethod, { 82 | shared: true, 83 | accepts: extend(argSpec, {http: {source: source}}), 84 | returns: {name: 'success', type: 'boolean'}, 85 | }); 86 | 87 | let uri = '/testClass/testMethod'; 88 | let chain; // eslint-disable-line one-var 89 | if (TARGET_QUERY_STRING) { 90 | uri = uri + '?' + queryString; 91 | chain = ctx.request.get(uri); 92 | } else { 93 | chain = ctx.request.post(uri) 94 | .type('form') 95 | .send(queryString); 96 | } 97 | 98 | chain 99 | .set('Accept', 'application/json') 100 | .expect('Content-Type', /json/) 101 | .end(function(err, res) { 102 | ctx.verifyResultOnResponse(err, res, argValue, expectedResult, done); 103 | }); 104 | } 105 | }; 106 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonbody-any.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonBodyContext = require('./_jsonbody.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonBodyContext(ctx); 12 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 13 | const verifyTestCases = ctx.verifyTestCases; 14 | 15 | describe('json body - any - required', function() { 16 | // See verifyTestCases' jsdoc for details about the format of test cases. 17 | verifyTestCases({arg: 'anyname', type: 'any', required: true}, [ 18 | [null, ERROR_BAD_REQUEST], 19 | // Both empty array and empty object are valid values for "any" 20 | [[]], 21 | [{}], 22 | // Other valid values 23 | [false], 24 | [1], 25 | // To send a string in a JSON body, one has to manually encode it, 26 | // because supertest sends string data verbatim 27 | ['"text"', 'text'], 28 | [{x: null}], 29 | [[1]], 30 | ]); 31 | }); 32 | 33 | describe('json body - any - optional', function() { 34 | // See verifyTestCases' jsdoc for details about the format of test cases. 35 | verifyTestCases({arg: 'anyname', type: 'any'}, [ 36 | // Empty values 37 | [null, null], 38 | 39 | // Valid values 40 | [false], 41 | [1], 42 | ['"text"', 'text'], 43 | 44 | // Dates are not recognized/parsed 45 | ['"2016-05-19T13:28:51.299Z"', '2016-05-19T13:28:51.299Z'], 46 | 47 | [[]], 48 | [{}], 49 | 50 | // Verify that deep coercion is not triggered 51 | // and types specified in JSON are preserved 52 | 53 | [{x: null}], 54 | [[null]], 55 | [{x: 'null'}], 56 | [['null']], 57 | 58 | [{x: false}], 59 | [[false]], 60 | [{x: 'false'}], 61 | [['false']], 62 | 63 | [{x: ''}], 64 | [['']], 65 | 66 | [{x: true}], 67 | [[true]], 68 | [{x: 'true'}], 69 | [['true']], 70 | 71 | [{x: 0}], 72 | [[0]], 73 | [{x: '0'}], 74 | [['0']], 75 | 76 | [{x: 1}], 77 | [[1]], 78 | [{x: '1'}], 79 | [['1']], 80 | 81 | [{x: -1}], 82 | [[-1]], 83 | [{x: '-1'}], 84 | [['-1']], 85 | 86 | [{x: 1.2}], 87 | [[1.2]], 88 | [{x: '1.2'}], 89 | [['1.2']], 90 | 91 | [{x: -1.2}], 92 | [[-1.2]], 93 | [{x: '-1.2'}], 94 | [['-1.2']], 95 | 96 | [{x: 'text'}], 97 | [['text']], 98 | 99 | [{x: []}], 100 | [[[]]], 101 | [{x: '[]'}], 102 | [['[]']], 103 | 104 | [{x: {}}], 105 | [[{}]], 106 | [{x: '{}'}], 107 | [['{}']], 108 | 109 | // Numeric strings larger than MAX_SAFE_INTEGER 110 | [{x: '2343546576878989879789'}], 111 | [['2343546576878989879789']], 112 | [{x: '-2343546576878989879789'}], 113 | [['-2343546576878989879789']], 114 | 115 | // Strings mimicking scientific notation 116 | [{x: '1.234e+30'}], 117 | [['1.234e+30']], 118 | [{x: '-1.234e+30'}], 119 | [['-1.234e+30']], 120 | 121 | // Should `any` recognize date? 122 | [{x: '2016-05-19T13:28:51.299Z'}], 123 | [['2016-05-19T13:28:51.299Z']], 124 | [{x: '2016-05-19'}], 125 | [['2016-05-19']], 126 | [{x: 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'}], 127 | [['Thu May 19 2016 15:28:51 GMT 0200 (CEST)']], 128 | ]); 129 | }); 130 | }; 131 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonbody-geopoint.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const GeoPoint = require('loopback-datatype-geopoint'); 9 | const jsonBodyContext = require('./_jsonbody.context'); 10 | 11 | module.exports = function(ctx) { 12 | ctx = jsonBodyContext(ctx); 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json body - geopoint - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'anyname', type: 'geopoint', required: true}, [ 19 | // valid values 20 | [{lat: 2.5, lng: 3.2}, new GeoPoint(2.5, 3.2)], 21 | [[2.5, 3.2], new GeoPoint(2.5, 3.2)], // Arrays are allowed 22 | 23 | // Empty values trigger ERROR_BAD_REQUEST 24 | [null, ERROR_BAD_REQUEST], 25 | [undefined, ERROR_BAD_REQUEST], 26 | [{}, ERROR_BAD_REQUEST], 27 | [[], ERROR_BAD_REQUEST], 28 | ]); 29 | }); 30 | 31 | describe('json body - geopoint - optional', function() { 32 | // See verifyTestCases' jsdoc for details about the format of test cases. 33 | verifyTestCases({arg: 'anyname', type: 'geopoint'}, [ 34 | 35 | // Valid values 36 | [{lat: 0, lng: 3.2}, new GeoPoint(0, 3.2)], 37 | [{lat: -1, lng: 3.2}, new GeoPoint(-1, 3.2)], 38 | [{lat: 1, lng: 3.2}, new GeoPoint(1, 3.2)], 39 | [{lat: 2.5, lng: 0}, new GeoPoint(2.5, 0)], 40 | [{lat: 2.5, lng: -1}, new GeoPoint(2.5, -1)], 41 | 42 | // Scientific notation works 43 | [{lat: 1.234e+1, lng: -1.234e+1}, new GeoPoint(1.234e+1, -1.234e+1)], 44 | 45 | // Missing values trigger ERROR_BAD_REQUEST 46 | [{lat: 2}, ERROR_BAD_REQUEST], 47 | 48 | // Invalid values trigger ERROR_BAD_REQUEST 49 | [null, ERROR_BAD_REQUEST], 50 | [{lat: null, lng: 2}, ERROR_BAD_REQUEST], 51 | [{lat: 2.5, lng: undefined}, ERROR_BAD_REQUEST], 52 | [{lat: NaN, lng: 3.2}, ERROR_BAD_REQUEST], 53 | 54 | // Latitude beyond range: +/-90 triggers ERROR_BAD_REQUEST 55 | [{lat: -91, lng: 3.2}, ERROR_BAD_REQUEST], 56 | [{lat: 90.521, lng: 3.2}, ERROR_BAD_REQUEST], 57 | 58 | // Longitude beyond range: +/-180 triggers ERROR_BAD_REQUEST 59 | [{lat: 2.5, lng: -181}, ERROR_BAD_REQUEST], 60 | [{lat: 2.5, lng: 180.45}, ERROR_BAD_REQUEST], 61 | 62 | // String is not supported 63 | [{lat: 'text', lng: 3.2}, ERROR_BAD_REQUEST], 64 | [{lat: 2.5, lng: 'text'}, ERROR_BAD_REQUEST], 65 | [{lat: '', lng: 3.2}, ERROR_BAD_REQUEST], 66 | // String of numbers 67 | [{lat: '33', lng: 3.2}, ERROR_BAD_REQUEST], 68 | // String mimicking scientific notation 69 | [{lat: '1.234e+1', lng: 3.2}, ERROR_BAD_REQUEST], 70 | 71 | // boolean is not supported 72 | [{lat: true, lng: 3.2}, ERROR_BAD_REQUEST], 73 | [{lat: -9.5, lng: false}, ERROR_BAD_REQUEST], 74 | 75 | // Array with more than two elements is not allowed 76 | [[2, 3, 5], ERROR_BAD_REQUEST], 77 | // Array of objects is not allowed 78 | [[{}, {}], ERROR_BAD_REQUEST], 79 | ]); 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonbody-object-type.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonBodyContext = require('./_jsonbody.context'); 9 | const customClassContext = require('./_custom-class.context.js'); 10 | 11 | module.exports = function(ctx) { 12 | ctx = customClassContext(jsonBodyContext(ctx)); 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const CustomClass = ctx.CustomClass; 15 | const verifyTestCases = ctx.verifyTestCases; 16 | 17 | describe('json body - CustomClass - required', function() { 18 | // See verifyTestCases' jsdoc for details about the format of test cases. 19 | verifyTestCases({arg: 'anyname', type: 'CustomClass', required: true}, [ 20 | // An empty object is a valid value 21 | [{}], 22 | 23 | [{name: ''}], 24 | [{name: 'a-test-name'}], 25 | 26 | // Invalid values trigger ERROR_BAD_REQUEST 27 | [null, ERROR_BAD_REQUEST], 28 | [{invalid: true}, ERROR_BAD_REQUEST], 29 | 30 | // Array values are not allowed 31 | [[], ERROR_BAD_REQUEST], 32 | [[1, 2], ERROR_BAD_REQUEST], 33 | ]); 34 | }); 35 | 36 | describe('json body - CustomClass - optional', function() { 37 | // See verifyTestCases' jsdoc for details about the format of test cases. 38 | verifyTestCases({arg: 'anyname', type: 'CustomClass'}, [ 39 | // Empty values 40 | [null, null], 41 | 42 | // Valid values 43 | [{}], 44 | [{name: 'a-test-name'}], 45 | 46 | // Verify that deep coercion is not triggered 47 | // and types specified in JSON are preserved 48 | 49 | [{name: ''}], 50 | [{name: null}], 51 | [{name: {}}], 52 | [{name: {key: null}}], 53 | [{name: 1}], 54 | [{name: '1'}], 55 | [{name: -1}], 56 | [{name: '-1'}], 57 | [{name: 1.2}], 58 | [{name: '1.2'}], 59 | [{name: -1.2}], 60 | [{name: '-1.2'}], 61 | [{name: ['tenamet']}], 62 | [{name: [1, 2]}], 63 | 64 | // Invalid values - arrays are rejected 65 | [[], ERROR_BAD_REQUEST], 66 | [[1, 2], ERROR_BAD_REQUEST], 67 | 68 | // Verify that errors thrown by the factory function are handled 69 | [{invalid: true}, ERROR_BAD_REQUEST], 70 | ]); 71 | }); 72 | 73 | describe('json body - CustomClass - allowArray: true', function() { 74 | verifyTestCases({arg: 'anyname', type: 'CustomClass', allowArray: true}, [ 75 | // normal objects is valid 76 | [{x: ''}], 77 | [{x: null}], 78 | [{x: {}}], 79 | [{x: {key: null}}], 80 | 81 | // array of objects also valid 82 | [[{}]], 83 | [[{x: ''}]], 84 | [[{x: null}]], 85 | [[{x: 1}, {y: 'string'}]], 86 | 87 | // array of non-objects are invalid 88 | [[{}, [{}]], ERROR_BAD_REQUEST], 89 | [[{}, 3.1415], ERROR_BAD_REQUEST], 90 | [[{}, 'non-object'], ERROR_BAD_REQUEST], 91 | ]); 92 | }); 93 | }; 94 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonbody-object.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonBodyContext = require('./_jsonbody.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonBodyContext(ctx); 12 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 13 | const verifyTestCases = ctx.verifyTestCases; 14 | 15 | describe('json body - object - required', function() { 16 | // See verifyTestCases' jsdoc for details about the format of test cases. 17 | verifyTestCases({arg: 'anyname', type: 'object', required: true}, [ 18 | // Valid values, arrays are objects too 19 | [{}], // an empty object is a valid value too 20 | [{x: ''}], 21 | [{x: null}], 22 | 23 | // Invalid values trigger ERROR_BAD_REQUEST 24 | [null, ERROR_BAD_REQUEST], 25 | 26 | // Arrays are not allowed 27 | [[], ERROR_BAD_REQUEST], 28 | [[1, 2], ERROR_BAD_REQUEST], 29 | ]); 30 | }); 31 | 32 | describe('json body - object - optional', function() { 33 | // See verifyTestCases' jsdoc for details about the format of test cases. 34 | verifyTestCases({arg: 'anyname', type: 'object'}, [ 35 | // Empty values 36 | [null, null], 37 | 38 | // Valid values 39 | [{}], 40 | 41 | // Verify that deep coercion is not triggered 42 | // and types specified in JSON are preserved 43 | 44 | [{x: ''}], 45 | [{x: null}], 46 | [{x: {}}], 47 | [{x: {key: null}}], 48 | [{x: 'value'}], 49 | [{x: 1}], 50 | [{x: '1'}], 51 | [{x: -1}], 52 | [{x: '-1'}], 53 | [{x: 1.2}], 54 | [{x: '1.2'}], 55 | [{x: -1.2}], 56 | [{x: '-1.2'}], 57 | [{x: ['text']}], 58 | [{x: [1, 2]}], 59 | 60 | // Numeric strings larger than MAX_SAFE_INTEGER 61 | [{x: '2343546576878989879789'}], 62 | [{x: '-2343546576878989879789'}], 63 | 64 | // Strings mimicking scientific notation 65 | [{x: '1.234e+30'}], 66 | [{x: '-1.234e+30'}], 67 | 68 | // Should we deep-coerce date values? 69 | [{x: '2016-05-19T13:28:51.299Z'}], 70 | [{x: '2016-05-19'}], 71 | [{x: 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'}], 72 | 73 | // Arrays are not allowed 74 | [[], ERROR_BAD_REQUEST], 75 | [[1, 2], ERROR_BAD_REQUEST], 76 | ]); 77 | }); 78 | 79 | describe('json body - object - allowArray: true', function() { 80 | verifyTestCases({arg: 'data', type: 'object', allowArray: true}, [ 81 | // normal objects is valid 82 | [{x: ''}], 83 | [{x: null}], 84 | [{x: {}}], 85 | [{x: {key: null}}], 86 | 87 | // array of objects also valid 88 | [[{}]], 89 | [[{x: ''}]], 90 | [[{x: null}]], 91 | [[{x: 1}, {y: 'string'}]], 92 | 93 | // array of non-objects are invalid 94 | [[{}, [{}]], ERROR_BAD_REQUEST], 95 | [[{}, 3.1415], ERROR_BAD_REQUEST], 96 | [[{}, 'non-object'], ERROR_BAD_REQUEST], 97 | ]); 98 | }); 99 | }; 100 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-any.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - any - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'any', required: true}, [ 19 | // Valid values 20 | [{arg: 1234}, 1234], 21 | [{arg: 'text'}, 'text'], 22 | [{arg: 'undefined'}, 'undefined'], 23 | [{arg: 'null'}, 'null'], 24 | // Invalid (empty) values should trigger ERROR_BAD_REQUEST 25 | [EMPTY_BODY, ERROR_BAD_REQUEST], 26 | [{arg: ''}, ERROR_BAD_REQUEST], 27 | [{arg: null}, ERROR_BAD_REQUEST], 28 | ]); 29 | }); 30 | 31 | describe('json form - any - optional', function() { 32 | // See verifyTestCases' jsdoc for details about the format of test cases. 33 | verifyTestCases({arg: 'arg', type: 'any'}, [ 34 | // Empty values 35 | [EMPTY_BODY, undefined], 36 | [{arg: null}, null], 37 | [{arg: ''}, ''], 38 | 39 | // Valid values 40 | [{arg: false}, false], 41 | [{arg: true}, true], 42 | [{arg: 0}, 0], 43 | [{arg: 1}, 1], 44 | [{arg: -1}, -1], 45 | [{arg: 1.2}, 1.2], 46 | [{arg: -1.2}, -1.2], 47 | [{arg: 'text'}, 'text'], 48 | [{arg: []}, []], 49 | [{arg: {}}, {}], 50 | 51 | // Should `any` recognize date format? 52 | [{arg: '2016-05-19T13:28:51.299Z'}, '2016-05-19T13:28:51.299Z'], 53 | [{arg: '2016-05-19'}, '2016-05-19'], 54 | [{arg: 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'}, 55 | 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'], 56 | 57 | // Verify that deep coercion is not triggered 58 | // and types specified in JSON are preserved 59 | [{arg: 'null'}, 'null'], 60 | [{arg: 'false'}, 'false'], 61 | [{arg: 'true'}, 'true'], 62 | [{arg: '0'}, '0'], 63 | [{arg: '1'}, '1'], 64 | [{arg: '-1'}, '-1'], 65 | [{arg: '1.2'}, '1.2'], 66 | [{arg: '-1.2'}, '-1.2'], 67 | [{arg: '[]'}, '[]'], 68 | [{arg: '{}'}, '{}'], 69 | 70 | // Numberic strings larger than MAX_SAFE_INTEGER 71 | [{arg: '2343546576878989879789'}, '2343546576878989879789'], 72 | [{arg: '-2343546576878989879789'}, '-2343546576878989879789'], 73 | 74 | // Strings mimicking scientific notation 75 | [{arg: '1.234e+30'}, '1.234e+30'], 76 | [{arg: '-1.234e+30'}, '-1.234e+30'], 77 | ]); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-boolean.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - boolean - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'boolean', required: true}, [ 19 | // Valid values 20 | [{arg: false}, false], 21 | [{arg: true}, true], 22 | 23 | // Empty values should trigger ERROR_BAD_REQUEST 24 | [EMPTY_BODY, ERROR_BAD_REQUEST], 25 | [{arg: null}, ERROR_BAD_REQUEST], 26 | [{arg: ''}, ERROR_BAD_REQUEST], 27 | ]); 28 | }); 29 | 30 | describe('json form - boolean - optional', function() { 31 | // See verifyTestCases' jsdoc for details about the format of test cases. 32 | verifyTestCases({arg: 'arg', type: 'boolean'}, [ 33 | // Empty values 34 | [EMPTY_BODY, undefined], 35 | 36 | // Valid values 37 | [{arg: false}, false], 38 | [{arg: true}, true], 39 | 40 | // Invalid values should trigger ERROR_BAD_REQUEST 41 | [{arg: null}, ERROR_BAD_REQUEST], 42 | [{arg: ''}, ERROR_BAD_REQUEST], 43 | [{arg: 'null'}, ERROR_BAD_REQUEST], 44 | [{arg: 'false'}, ERROR_BAD_REQUEST], 45 | [{arg: 'true'}, ERROR_BAD_REQUEST], 46 | [{arg: 0}, ERROR_BAD_REQUEST], 47 | [{arg: '0'}, ERROR_BAD_REQUEST], 48 | [{arg: 1}, ERROR_BAD_REQUEST], 49 | [{arg: '1'}, ERROR_BAD_REQUEST], 50 | [{arg: 'text'}, ERROR_BAD_REQUEST], 51 | [{arg: []}, ERROR_BAD_REQUEST], 52 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 53 | [{arg: {}}, ERROR_BAD_REQUEST], 54 | [{arg: {a: true}}, ERROR_BAD_REQUEST], 55 | ]); 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-date.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | const INVALID_DATE = new Date(NaN); 11 | 12 | module.exports = function(ctx) { 13 | ctx = jsonFormContext(ctx); 14 | const EMPTY_BODY = ctx.EMPTY_BODY; 15 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 16 | const verifyTestCases = ctx.verifyTestCases; 17 | 18 | describe('json form - date - required', function() { 19 | // See verifyTestCases' jsdoc for details about the format of test cases. 20 | verifyTestCases({arg: 'arg', type: 'date', required: true}, [ 21 | // Valid values 22 | [{arg: 0}, new Date(0)], 23 | [{arg: '0'}, new Date('0')], 24 | [{arg: '2016-05-19T13:28:51.299Z'}, 25 | new Date('2016-05-19T13:28:51.299Z')], 26 | 27 | // Empty values should trigger ERROR_BAD_REQUEST 28 | [EMPTY_BODY, ERROR_BAD_REQUEST], 29 | [{arg: null}, ERROR_BAD_REQUEST], 30 | [{arg: ''}, ERROR_BAD_REQUEST], 31 | ]); 32 | }); 33 | 34 | describe('json form - date - optional', function() { 35 | // See verifyTestCases' jsdoc for details about the format of test cases. 36 | verifyTestCases({arg: 'arg', type: 'date'}, [ 37 | // Empty cases 38 | [EMPTY_BODY, undefined], 39 | 40 | // Valid values - ISO format 41 | [{arg: '2016-05-19T13:28:51.299Z'}, new Date('2016-05-19T13:28:51.299Z')], 42 | [{arg: '2016-05-19'}, new Date('2016-05-19')], 43 | [{arg: 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'}, 44 | new Date('2016-05-19T15:28:51.000Z')], 45 | 46 | // Valid values - milliseconds from Unix Epoch 47 | [{arg: 0}, new Date(0)], 48 | [{arg: 1}, new Date(1)], 49 | [{arg: -1}, new Date(-1)], 50 | [{arg: 1.2}, new Date(1.2)], 51 | [{arg: -1.2}, new Date(-1.2)], 52 | 53 | // Valid values - numeric strings 54 | [{arg: '0'}, new Date('0')], 55 | [{arg: '1'}, new Date('1')], 56 | [{arg: '-1'}, new Date('-1')], 57 | [{arg: '1.2'}, new Date('1.2')], 58 | [{arg: '-1.2'}, new Date('-1.2')], 59 | 60 | // Invalid values should trigger ERROR_BAD_REQUEST 61 | [{arg: null}, ERROR_BAD_REQUEST], 62 | [{arg: ''}, ERROR_BAD_REQUEST], 63 | [{arg: 'null'}, ERROR_BAD_REQUEST], 64 | [{arg: false}, ERROR_BAD_REQUEST], 65 | [{arg: 'false'}, ERROR_BAD_REQUEST], 66 | [{arg: true}, ERROR_BAD_REQUEST], 67 | [{arg: 'true'}, ERROR_BAD_REQUEST], 68 | [{arg: 'text'}, ERROR_BAD_REQUEST], 69 | [{arg: []}, ERROR_BAD_REQUEST], 70 | [{arg: {}}, ERROR_BAD_REQUEST], 71 | 72 | // Numbers larger than MAX_SAFE_INTEGER - should cause ERROR_BAD_REQUEST 73 | [{arg: 2343546576878989879789}, ERROR_BAD_REQUEST], 74 | [{arg: -2343546576878989879789}, ERROR_BAD_REQUEST], 75 | // Scientific notation - should cause ERROR_BAD_REQUEST 76 | [{arg: 1.234e+30}, ERROR_BAD_REQUEST], 77 | [{arg: -1.234e+30}, ERROR_BAD_REQUEST], 78 | ]); 79 | }); 80 | }; 81 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-geopoint.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const GeoPoint = require('loopback-datatype-geopoint'); 9 | const jsonFormContext = require('./_jsonform.context'); 10 | 11 | module.exports = function(ctx) { 12 | ctx = jsonFormContext(ctx); 13 | const EMPTY_BODY = ctx.EMPTY_BODY; 14 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 15 | const verifyTestCases = ctx.verifyTestCases; 16 | 17 | describe('json form - geopoint - required', function() { 18 | // See verifyTestCases' jsdoc for details about the format of test cases. 19 | verifyTestCases({arg: 'arg', type: 'geopoint', required: true}, [ 20 | // Valid values 21 | [{arg: {lat: 2.5, lng: 3.2}}, new GeoPoint(2.5, 3.2)], 22 | [{arg: [2.5, 3.2]}, new GeoPoint(2.5, 3.2)], 23 | 24 | // Empty values trigger ERROR_BAD_REQUEST 25 | [EMPTY_BODY, ERROR_BAD_REQUEST], 26 | [{arg: null}, ERROR_BAD_REQUEST], 27 | [{arg: ''}, ERROR_BAD_REQUEST], 28 | [{arg: {}}, ERROR_BAD_REQUEST], 29 | [{arg: []}, ERROR_BAD_REQUEST], 30 | ]); 31 | }); 32 | 33 | describe('json form - geopoint - optional', function() { 34 | // See verifyTestCases' jsdoc for details about the format of test cases. 35 | verifyTestCases({arg: 'arg', type: 'geopoint'}, [ 36 | // Empty values 37 | [EMPTY_BODY, undefined], 38 | 39 | // Valid values 40 | [{arg: {lat: 0, lng: 3.2}}, new GeoPoint(0, 3.2)], 41 | [{arg: {lat: -1, lng: 3.2}}, new GeoPoint(-1, 3.2)], 42 | [{arg: {lat: 1, lng: 3.2}}, new GeoPoint(1, 3.2)], 43 | [{arg: {lat: 2.5, lng: 0}}, new GeoPoint(2.5, 0)], 44 | [{arg: {lat: 2.5, lng: -1}}, new GeoPoint(2.5, -1)], 45 | // Scientific notation works 46 | [{arg: {lat: 1.234e+1, lng: -1.234e+1}}, new GeoPoint(1.234e+1, -1.234e+1)], 47 | 48 | // Missing values trigger ERROR_BAD_REQUEST 49 | [{arg: {lat: 2}}, ERROR_BAD_REQUEST], 50 | 51 | // Invalid values trigger ERROR_BAD_REQUEST 52 | [{arg: {lat: null, lng: 2}}, ERROR_BAD_REQUEST], 53 | [{arg: {lat: 2.5, lng: undefined}}, ERROR_BAD_REQUEST], 54 | [{arg: {lat: NaN, lng: 3.2}}, ERROR_BAD_REQUEST], 55 | 56 | // Latitude beyond range: +/-90 triggers ERROR_BAD_REQUEST 57 | [{arg: {lat: -91, lng: 3.2}}, ERROR_BAD_REQUEST], 58 | [{arg: {lat: 90.521, lng: 3.2}}, ERROR_BAD_REQUEST], 59 | 60 | // Longitude beyond range: +/-180 triggers ERROR_BAD_REQUEST 61 | [{arg: {lat: 2.5, lng: -181}}, ERROR_BAD_REQUEST], 62 | [{arg: {lat: 2.5, lng: 180.45}}, ERROR_BAD_REQUEST], 63 | 64 | // String is not supported 65 | [{arg: {lat: 'text', lng: 3.2}}, ERROR_BAD_REQUEST], 66 | [{arg: {lat: 2.5, lng: 'text'}}, ERROR_BAD_REQUEST], 67 | [{arg: {lat: '', lng: 3.2}}, ERROR_BAD_REQUEST], 68 | // String of numbers 69 | [{arg: {lat: '33', lng: 3.2}}, ERROR_BAD_REQUEST], 70 | // Strings mimicking scientific notation 71 | [{arg: {lat: '1.234e+1', lng: 3.2}}, ERROR_BAD_REQUEST], 72 | 73 | // boolean is not supported 74 | [{arg: {lat: true, lng: 3.2}}, ERROR_BAD_REQUEST], 75 | [{arg: {lat: -9.5, lng: false}}, ERROR_BAD_REQUEST], 76 | 77 | // Array with more than two elements is not allowed 78 | [{arg: [2, 3, 5]}, ERROR_BAD_REQUEST], 79 | // Array of objects is not allowed 80 | [{arg: [{}, {}]}, ERROR_BAD_REQUEST], 81 | ]); 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-integer.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - integer - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'integer', required: true}, [ 19 | // Valid values 20 | [{arg: 0}, 0], 21 | [{arg: 1}, 1], 22 | [{arg: -1}, -1], 23 | 24 | // Empty values should trigger ERROR_BAD_REQUEST 25 | [EMPTY_BODY, ERROR_BAD_REQUEST], 26 | [{arg: null}, ERROR_BAD_REQUEST], 27 | [{arg: ''}, ERROR_BAD_REQUEST], 28 | ]); 29 | }); 30 | 31 | describe('json form - integer - optional', function() { 32 | // See verifyTestCases' jsdoc for details about the format of test cases. 33 | verifyTestCases({arg: 'arg', type: 'integer'}, [ 34 | // Empty values 35 | [EMPTY_BODY, undefined], 36 | 37 | // Valid values 38 | [{arg: 0}, 0], 39 | [{arg: 1}, 1], 40 | [{arg: -1}, -1], 41 | 42 | // Integers larger than MAX_SAFE_INTEGER should trigger ERROR_BAD_REQUEST 43 | [{arg: 2343546576878989879789}, ERROR_BAD_REQUEST], 44 | [{arg: -2343546576878989879789}, ERROR_BAD_REQUEST], 45 | 46 | // Scientific notation works 47 | [{arg: 1.234e+3}, 1.234e+3], 48 | [{arg: -1.234e+3}, -1.234e+3], 49 | 50 | // Integer-like string values should trigger ERROR_BAD_REQUEST 51 | [{arg: '0'}, ERROR_BAD_REQUEST], 52 | [{arg: '1'}, ERROR_BAD_REQUEST], 53 | [{arg: '-1'}, ERROR_BAD_REQUEST], 54 | [{arg: '1.2'}, ERROR_BAD_REQUEST], 55 | [{arg: '-1.2'}, ERROR_BAD_REQUEST], 56 | [{arg: '2343546576878989879789'}, ERROR_BAD_REQUEST], 57 | [{arg: '-2343546576878989879789'}, ERROR_BAD_REQUEST], 58 | [{arg: '1.234e+30'}, ERROR_BAD_REQUEST], 59 | [{arg: '-1.234e+30'}, ERROR_BAD_REQUEST], 60 | 61 | // All other non-integer values should trigger ERROR_BAD_REQUEST 62 | [{arg: null}, ERROR_BAD_REQUEST], 63 | [{arg: 1.2}, ERROR_BAD_REQUEST], 64 | [{arg: -1.2}, ERROR_BAD_REQUEST], 65 | [{arg: ''}, ERROR_BAD_REQUEST], 66 | [{arg: false}, ERROR_BAD_REQUEST], 67 | [{arg: 'false'}, ERROR_BAD_REQUEST], 68 | [{arg: true}, ERROR_BAD_REQUEST], 69 | [{arg: 'true'}, ERROR_BAD_REQUEST], 70 | [{arg: 'text'}, ERROR_BAD_REQUEST], 71 | [{arg: []}, ERROR_BAD_REQUEST], 72 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 73 | [{arg: {}}, ERROR_BAD_REQUEST], 74 | [{arg: {a: true}}, ERROR_BAD_REQUEST], 75 | ]); 76 | }); 77 | }; 78 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-number.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - number - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'number', required: true}, [ 19 | // Valid values 20 | [{arg: 0}, 0], 21 | [{arg: 1}, 1], 22 | [{arg: -1}, -1], 23 | 24 | // Empty values should trigger ERROR_BAD_REQUEST 25 | [EMPTY_BODY, ERROR_BAD_REQUEST], 26 | [{arg: null}, ERROR_BAD_REQUEST], 27 | [{arg: ''}, ERROR_BAD_REQUEST], 28 | ]); 29 | }); 30 | 31 | describe('json form - number - optional', function() { 32 | // See verifyTestCases' jsdoc for details about the format of test cases. 33 | verifyTestCases({arg: 'arg', type: 'number'}, [ 34 | // Empty values 35 | [EMPTY_BODY, undefined], 36 | 37 | // Valid values 38 | [{arg: 0}, 0], 39 | [{arg: 1}, 1], 40 | [{arg: -1}, -1], 41 | [{arg: 1.2}, 1.2], 42 | [{arg: -1.2}, -1.2], 43 | 44 | // Numbers larger than MAX_SAFE_INTEGER get trimmed 45 | [{arg: 2343546576878989879789}, 2.34354657687899e+21], 46 | [{arg: -2343546576878989879789}, -2.34354657687899e+21], 47 | 48 | // Scientific notation works 49 | [{arg: 1.234e+30}, 1.234e+30], 50 | [{arg: -1.234e+30}, -1.234e+30], 51 | 52 | // Number-like string values should trigger ERROR_BAD_REQUEST 53 | [{arg: '0'}, ERROR_BAD_REQUEST], 54 | [{arg: '1'}, ERROR_BAD_REQUEST], 55 | [{arg: '-1'}, ERROR_BAD_REQUEST], 56 | [{arg: '1.2'}, ERROR_BAD_REQUEST], 57 | [{arg: '-1.2'}, ERROR_BAD_REQUEST], 58 | [{arg: '2343546576878989879789'}, ERROR_BAD_REQUEST], 59 | [{arg: '-2343546576878989879789'}, ERROR_BAD_REQUEST], 60 | [{arg: '1.234e+30'}, ERROR_BAD_REQUEST], 61 | [{arg: '-1.234e+30'}, ERROR_BAD_REQUEST], 62 | 63 | // All other non-number values should trigger ERROR_BAD_REQUEST 64 | [{arg: null}, ERROR_BAD_REQUEST], 65 | [{arg: ''}, ERROR_BAD_REQUEST], 66 | [{arg: false}, ERROR_BAD_REQUEST], 67 | [{arg: 'false'}, ERROR_BAD_REQUEST], 68 | [{arg: true}, ERROR_BAD_REQUEST], 69 | [{arg: 'true'}, ERROR_BAD_REQUEST], 70 | [{arg: 'text'}, ERROR_BAD_REQUEST], 71 | [{arg: []}, ERROR_BAD_REQUEST], 72 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 73 | [{arg: {}}, ERROR_BAD_REQUEST], 74 | [{arg: {a: true}}, ERROR_BAD_REQUEST], 75 | ]); 76 | }); 77 | }; 78 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-object-type.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | const customClassContext = require('./_custom-class.context.js'); 10 | 11 | module.exports = function(ctx) { 12 | ctx = customClassContext(jsonFormContext(ctx)); 13 | const EMPTY_BODY = ctx.EMPTY_BODY; 14 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 15 | const CustomClass = ctx.CustomClass; 16 | const verifyTestCases = ctx.verifyTestCases; 17 | 18 | describe('json form - CustomClass - required', function() { 19 | // See verifyTestCases' jsdoc for details about the format of test cases. 20 | verifyTestCases({arg: 'arg', type: 'CustomClass', required: true}, [ 21 | // Valid values 22 | [{arg: {}}, CustomClass({})], 23 | [{arg: {foo: 'bar'}}, CustomClass({foo: 'bar'})], 24 | 25 | // Empty values should trigger ERROR_BAD_REQUEST 26 | [EMPTY_BODY, ERROR_BAD_REQUEST], 27 | [{arg: null}, ERROR_BAD_REQUEST], 28 | [{arg: ''}, ERROR_BAD_REQUEST], 29 | 30 | // Arrays are not allowed 31 | [{arg: []}, ERROR_BAD_REQUEST], 32 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 33 | ]); 34 | }); 35 | 36 | describe('json form - CustomClass - optional', function() { 37 | // See verifyTestCases' jsdoc for details about the format of test cases. 38 | verifyTestCases({arg: 'arg', type: 'CustomClass'}, [ 39 | // Empty values 40 | [EMPTY_BODY, undefined], 41 | [{arg: null}, null], 42 | 43 | // Valid values 44 | [{arg: {name: null}}, CustomClass({name: null})], 45 | [{arg: {}}, CustomClass({})], 46 | [{arg: {name: 'value'}}, CustomClass({name: 'value'})], 47 | [{arg: {name: 1}}, CustomClass({name: 1})], 48 | 49 | // Verify that deep coercion is not triggered 50 | // and types specified in JSON are preserved 51 | [{arg: {name: '1'}}, CustomClass({name: '1'})], 52 | [{arg: {name: -1}}, CustomClass({name: -1})], 53 | [{arg: {name: '-1'}}, CustomClass({name: '-1'})], 54 | [{arg: {name: 1.2}}, CustomClass({name: 1.2})], 55 | [{arg: {name: '1.2'}}, CustomClass({name: '1.2'})], 56 | [{arg: {name: -1.2}}, CustomClass({name: -1.2})], 57 | [{arg: {name: '-1.2'}}, CustomClass({name: '-1.2'})], 58 | [{arg: {name: 'true'}}, CustomClass({name: 'true'})], 59 | [{arg: {name: 'false'}}, CustomClass({name: 'false'})], 60 | 61 | // Invalid values should trigger ERROR_BAD_REQUEST 62 | [{arg: ''}, ERROR_BAD_REQUEST], 63 | [{arg: false}, ERROR_BAD_REQUEST], 64 | [{arg: true}, ERROR_BAD_REQUEST], 65 | [{arg: 0}, ERROR_BAD_REQUEST], 66 | [{arg: 1}, ERROR_BAD_REQUEST], 67 | [{arg: -1}, ERROR_BAD_REQUEST], 68 | 69 | // Arrays are not allowed 70 | [{arg: []}, ERROR_BAD_REQUEST], 71 | [{arg: ['text']}, ERROR_BAD_REQUEST], 72 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 73 | 74 | // Verify that errors thrown by the factory function are handled 75 | [{arg: {invalid: true}}, ERROR_BAD_REQUEST], 76 | ]); 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-object.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - object - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'object', required: true}, [ 19 | // Valid values 20 | [{arg: {}}, {}], 21 | [{arg: {foo: 'bar'}}, {foo: 'bar'}], 22 | 23 | // Empty values should trigger ERROR_BAD_REQUEST 24 | [EMPTY_BODY, ERROR_BAD_REQUEST], 25 | [{arg: null}, ERROR_BAD_REQUEST], 26 | [{arg: ''}, ERROR_BAD_REQUEST], 27 | 28 | // Arrays are not allowed 29 | [{arg: []}, ERROR_BAD_REQUEST], 30 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 31 | ]); 32 | }); 33 | 34 | describe('json form - object - optional', function() { 35 | // See verifyTestCases' jsdoc for details about the format of test cases. 36 | verifyTestCases({arg: 'arg', type: 'object'}, [ 37 | // Empty values 38 | [EMPTY_BODY, undefined], 39 | [{arg: null}, null], 40 | 41 | // Valid values 42 | [{arg: {x: null}}, {x: null}], 43 | [{arg: {}}, {}], 44 | [{arg: {x: 'value'}}, {x: 'value'}], 45 | [{arg: {x: 1}}, {x: 1}], 46 | 47 | // Verify that deep coercion is not triggered 48 | // and types specified in JSON are preserved 49 | [{arg: {x: '1'}}, {x: '1'}], 50 | [{arg: {x: -1}}, {x: -1}], 51 | [{arg: {x: '-1'}}, {x: '-1'}], 52 | [{arg: {x: 1.2}}, {x: 1.2}], 53 | [{arg: {x: '1.2'}}, {x: '1.2'}], 54 | [{arg: {x: -1.2}}, {x: -1.2}], 55 | [{arg: {x: '-1.2'}}, {x: '-1.2'}], 56 | [{arg: {x: 'true'}}, {x: 'true'}], 57 | [{arg: {x: 'false'}}, {x: 'false'}], 58 | 59 | // Invalid values should trigger ERROR_BAD_REQUEST 60 | [{arg: ''}, ERROR_BAD_REQUEST], 61 | [{arg: false}, ERROR_BAD_REQUEST], 62 | [{arg: true}, ERROR_BAD_REQUEST], 63 | [{arg: 0}, ERROR_BAD_REQUEST], 64 | [{arg: 1}, ERROR_BAD_REQUEST], 65 | [{arg: -1}, ERROR_BAD_REQUEST], 66 | 67 | // Arrays are not allowed 68 | [{arg: []}, ERROR_BAD_REQUEST], 69 | [{arg: ['text']}, ERROR_BAD_REQUEST], 70 | [{arg: [1, 2]}, ERROR_BAD_REQUEST], 71 | ]); 72 | }); 73 | 74 | describe('json form - object - allowArray: true', function() { 75 | verifyTestCases({arg: 'arg', type: 'object', allowArray: true}, [ 76 | // normal objects is valid 77 | [{arg: {x: null}}, {x: null}], 78 | [{arg: {}}, {}], 79 | [{arg: {x: 'value'}}, {x: 'value'}], 80 | [{arg: {x: 1}}, {x: 1}], 81 | 82 | // array of objects also valid 83 | [{arg: [{}]}, [{}]], 84 | [{arg: [{x: 1}, {}]}, [{x: 1}, {}]], 85 | [{arg: [{x: null}]}, [{x: null}]], 86 | 87 | // Invalid values should trigger ERROR_BAD_REQUEST 88 | [{arg: ''}, ERROR_BAD_REQUEST], 89 | [{arg: false}, ERROR_BAD_REQUEST], 90 | [{arg: true}, ERROR_BAD_REQUEST], 91 | [{arg: 0}, ERROR_BAD_REQUEST], 92 | [{arg: 1}, ERROR_BAD_REQUEST], 93 | [{arg: -1}, ERROR_BAD_REQUEST], 94 | 95 | // array of non-objects are invalid 96 | [{arg: [{}, [{}]]}, ERROR_BAD_REQUEST], 97 | [{arg: [{}, 3.1415]}, ERROR_BAD_REQUEST], 98 | [{arg: [{}, 'non-object']}, ERROR_BAD_REQUEST], 99 | ]); 100 | }); 101 | }; 102 | -------------------------------------------------------------------------------- /test/rest-coercion/jsonform-string.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const jsonFormContext = require('./_jsonform.context'); 9 | 10 | module.exports = function(ctx) { 11 | ctx = jsonFormContext(ctx); 12 | const EMPTY_BODY = ctx.EMPTY_BODY; 13 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 14 | const verifyTestCases = ctx.verifyTestCases; 15 | 16 | describe('json form - string - required', function() { 17 | // See verifyTestCases' jsdoc for details about the format of test cases. 18 | verifyTestCases({arg: 'arg', type: 'string', required: true}, [ 19 | // Valid values 20 | [{arg: 'null'}, 'null'], 21 | [{arg: 'text'}, 'text'], 22 | 23 | // Empty values should trigger ERROR_BAD_REQUEST 24 | [EMPTY_BODY, ERROR_BAD_REQUEST], 25 | [{arg: ''}, ERROR_BAD_REQUEST], 26 | [{arg: null}, ERROR_BAD_REQUEST], 27 | ]); 28 | }); 29 | 30 | describe('json form - string - optional', function() { 31 | // See verifyTestCases' jsdoc for details about the format of test cases. 32 | verifyTestCases({arg: 'arg', type: 'string'}, [ 33 | // Empty values 34 | [EMPTY_BODY, undefined], 35 | [{arg: ''}, ''], 36 | 37 | // Valid values 38 | [{arg: 'text'}, 'text'], 39 | 40 | // Verify that deep coercion is not triggered 41 | // and types specified in JSON are preserved 42 | [{arg: 'undefined'}, 'undefined'], 43 | [{arg: 'null'}, 'null'], 44 | [{arg: '0'}, '0'], 45 | [{arg: '1'}, '1'], 46 | [{arg: '-1'}, '-1'], 47 | [{arg: '1.2'}, '1.2'], 48 | [{arg: '-1.2'}, '-1.2'], 49 | [{arg: 'false'}, 'false'], 50 | [{arg: 'true'}, 'true'], 51 | 52 | // Invalid values should trigger ERROR_BAD_REQUEST 53 | [{arg: null}, ERROR_BAD_REQUEST], 54 | [{arg: false}, ERROR_BAD_REQUEST], 55 | [{arg: true}, ERROR_BAD_REQUEST], 56 | [{arg: 0}, ERROR_BAD_REQUEST], 57 | [{arg: 1}, ERROR_BAD_REQUEST], 58 | [{arg: -1}, ERROR_BAD_REQUEST], 59 | [{arg: 1.2}, ERROR_BAD_REQUEST], 60 | [{arg: -1.2}, ERROR_BAD_REQUEST], 61 | [{arg: []}, ERROR_BAD_REQUEST], 62 | [{arg: {}}, ERROR_BAD_REQUEST], 63 | ]); 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-any.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | module.exports = function(ctx) { 11 | suite('query string', urlEncodedContext(ctx, 'qs')); 12 | suite('form data', urlEncodedContext(ctx, 'form')); 13 | }; 14 | 15 | function suite(prefix, ctx) { 16 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 17 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 18 | const verifyTestCases = ctx.verifyTestCases; 19 | 20 | describe(prefix + ' - any - required', function() { 21 | // See verifyTestCases' jsdoc for details about the format of test cases. 22 | verifyTestCases({arg: 'arg', type: 'any', required: true}, [ 23 | // Valid values 24 | ['arg=1234', 1234], 25 | ['arg=text', 'text'], 26 | 27 | // Empty values should trigger ERROR_BAD_REQUEST 28 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 29 | ['arg', ERROR_BAD_REQUEST], 30 | ['arg=', ERROR_BAD_REQUEST], 31 | ['arg=null', ERROR_BAD_REQUEST], 32 | ]); 33 | }); 34 | 35 | describe(prefix + ' - any - optional', function() { 36 | // See verifyTestCases' jsdoc for details about the format of test cases. 37 | verifyTestCases({arg: 'arg', type: 'any'}, [ 38 | // Empty values 39 | [EMPTY_QUERY, undefined], 40 | ['arg', undefined], 41 | ['arg=', undefined], 42 | ['arg=null', null], // should be: 'null' 43 | 44 | // Valid values (coerced) 45 | ['arg=undefined', 'undefined'], // 'undefined' is treated as a string 46 | ['arg=false', false], 47 | ['arg=true', true], 48 | ['arg=0', 0], 49 | ['arg=1', 1], 50 | ['arg=-1', -1], 51 | ['arg=1.2', 1.2], 52 | ['arg=-1.2', -1.2], 53 | ['arg=text', 'text'], 54 | ['arg=[]', []], 55 | ['arg={}', {}], 56 | ['arg={"x":1}', {x: 1}], 57 | ['arg={"x":"1"}', {x: '1'}], 58 | ['arg={x:1}', '{x:1}'], // invalid JSON - the key is not quoted 59 | ['arg=[1]', [1]], 60 | ['arg=["1"]', ['1']], 61 | 62 | // Numbers larger than MAX_SAFE_INTEGER are treated as strings 63 | ['arg=2343546576878989879789', '2343546576878989879789'], 64 | ['arg=-2343546576878989879789', '-2343546576878989879789'], 65 | // Numbers starting with a leading zero are treated as strings 66 | // See https://github.com/strongloop/strong-remoting/issues/143 67 | ['arg=0668', '0668'], 68 | // However, floats are correctly parsed 69 | ['arg=0.42', 0.42], 70 | // Scientific notation should be recognized as a number 71 | ['arg=1.234e%2B30', '1.234e+30'], 72 | ['arg=-1.234e%2B30', '-1.234e+30'], 73 | // Should `any` recognize date format? 74 | ['arg=2016-05-19T13:28:51.299Z', '2016-05-19T13:28:51.299Z'], 75 | ['arg=2016-05-19', '2016-05-19'], 76 | ['arg=Thu+May+19+2016+15:28:51+GMT+0200+(CEST)', 77 | 'Thu May 19 2016 15:28:51 GMT 0200 (CEST)'], 78 | ]); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-boolean.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | module.exports = function(ctx) { 11 | suite('query string', urlEncodedContext(ctx, 'qs')); 12 | suite('form data', urlEncodedContext(ctx, 'form')); 13 | }; 14 | 15 | function suite(prefix, ctx) { 16 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 17 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 18 | const verifyTestCases = ctx.verifyTestCases; 19 | 20 | describe(prefix + ' - boolean - required', function() { 21 | // See verifyTestCases' jsdoc for details about the format of test cases. 22 | verifyTestCases({arg: 'arg', type: 'boolean', required: true}, [ 23 | // Valid values 24 | ['arg=false', false], 25 | ['arg=true', true], 26 | ['arg=0', false], 27 | 28 | // Empty values should trigger ERROR_BAD_REQUEST 29 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 30 | ['arg', ERROR_BAD_REQUEST], 31 | ['arg=', ERROR_BAD_REQUEST], 32 | 33 | // Empty-like values should trigger ERROR_BAD_REQUEST too 34 | ['arg=undefined', ERROR_BAD_REQUEST], 35 | ['arg=null', ERROR_BAD_REQUEST], 36 | ]); 37 | }); 38 | 39 | describe(prefix + ' - boolean - optional', function() { 40 | // See verifyTestCases' jsdoc for details about the format of test cases. 41 | verifyTestCases({arg: 'arg', type: 'boolean'}, [ 42 | // Empty values 43 | [EMPTY_QUERY, undefined], 44 | ['arg', undefined], 45 | ['arg=', undefined], 46 | 47 | // Valid values 48 | ['arg=false', false], 49 | ['arg=true', true], 50 | ['arg=0', false], 51 | ['arg=1', true], 52 | // values are case insensitive 53 | ['arg=FalsE', false], 54 | ['arg=TruE', true], 55 | ['arg=FALSE', false], 56 | ['arg=TRUE', true], 57 | 58 | // Invalid values should trigger ERROR_BAD_REQUEST 59 | ['arg=undefined', ERROR_BAD_REQUEST], 60 | ['arg=null', ERROR_BAD_REQUEST], 61 | ['arg=2', ERROR_BAD_REQUEST], 62 | ['arg=text', ERROR_BAD_REQUEST], 63 | ['arg=[]', ERROR_BAD_REQUEST], 64 | ['arg=[1,2]', ERROR_BAD_REQUEST], 65 | ['arg={}', ERROR_BAD_REQUEST], 66 | ['arg={"a":true}', ERROR_BAD_REQUEST], 67 | ]); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-date.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | const INVALID_DATE = new Date(NaN); 11 | 12 | module.exports = function(ctx) { 13 | suite('query string', urlEncodedContext(ctx, 'qs')); 14 | suite('form data', urlEncodedContext(ctx, 'form')); 15 | }; 16 | 17 | function suite(prefix, ctx) { 18 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 19 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 20 | const verifyTestCases = ctx.verifyTestCases; 21 | 22 | describe(prefix + ' - date - required', function() { 23 | // See verifyTestCases' jsdoc for details about the format of test cases. 24 | verifyTestCases({arg: 'arg', type: 'date', required: true}, [ 25 | // Valid values 26 | ['arg=2016-05-19T13:28:51.299Z', new Date('2016-05-19T13:28:51.299Z')], 27 | 28 | // Empty values should trigger ERROR_BAD_REQUEST 29 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 30 | ['arg', ERROR_BAD_REQUEST], 31 | ['arg=', ERROR_BAD_REQUEST], 32 | 33 | // Empty-like values should trigger ERROR_BAD_REQUEST too 34 | ['arg=undefined', ERROR_BAD_REQUEST], 35 | ['arg=null', ERROR_BAD_REQUEST], 36 | ]); 37 | }); 38 | 39 | describe(prefix + ' - date - optional', function() { 40 | // See verifyTestCases' jsdoc for details about the format of test cases. 41 | verifyTestCases({arg: 'arg', type: 'date'}, [ 42 | // Empty values 43 | [EMPTY_QUERY, undefined], 44 | ['arg', undefined], 45 | ['arg=', undefined], 46 | 47 | // Valid values - ISO format 48 | ['arg=2016-05-19T13:28:51.299Z', new Date('2016-05-19T13:28:51.299Z')], 49 | ['arg=2016-05-19', new Date('2016-05-19')], 50 | ['arg=Thu+May+19+2016+15:28:51+GMT+0200+(CEST)', 51 | new Date('2016-05-19T15:28:51.000Z')], 52 | 53 | // Integer values are converted to a number before passing it to 54 | // the Date constructor 55 | // That way ?arg=0 produces '1970-01-01T00:00:00.000Z', which is 56 | // arguably more expected then some date around 1999/2000/2001 57 | ['arg=0', new Date('1970-01-01T00:00:00.000Z')], 58 | ['arg=1', new Date('1970-01-01T00:00:00.001Z')], 59 | ['arg=-1', new Date('1969-12-31T23:59:59.999Z')], 60 | 61 | // Non-integer numbers are treated as strings. 62 | ['arg=1.2', new Date('1.2')], // 2001-01-01T23:00:00.000Z 63 | ['arg=-1.2', new Date('-1.2')], // 2001-01-01T23:00:00.000Z 64 | 65 | // Invalid values should trigger ERROR_BAD_REQUEST 66 | ['arg=null', ERROR_BAD_REQUEST], 67 | ['arg=undefined', ERROR_BAD_REQUEST], 68 | ['arg=false', ERROR_BAD_REQUEST], 69 | ['arg=true', ERROR_BAD_REQUEST], 70 | ['arg=text', ERROR_BAD_REQUEST], 71 | ['arg=[]', ERROR_BAD_REQUEST], 72 | ['arg={}', ERROR_BAD_REQUEST], 73 | // Numbers larger than MAX_SAFE_INTEGER - should cause ERROR_BAD_REQUEST 74 | ['arg=2343546576878989879789', ERROR_BAD_REQUEST], 75 | ['arg=-2343546576878989879789', ERROR_BAD_REQUEST], 76 | // Scientific notation - should cause ERROR_BAD_REQUEST 77 | ['arg=1.234e%2B30', ERROR_BAD_REQUEST], 78 | ['arg=-1.234e%2B30', ERROR_BAD_REQUEST], 79 | ]); 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-integer.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | module.exports = function(ctx) { 11 | suite('query string', urlEncodedContext(ctx, 'qs')); 12 | suite('form data', urlEncodedContext(ctx, 'form')); 13 | }; 14 | 15 | function suite(prefix, ctx) { 16 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 17 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 18 | const verifyTestCases = ctx.verifyTestCases; 19 | 20 | describe(prefix + ' - integer - required', function() { 21 | // See verifyTestCases' jsdoc for details about the format of test cases. 22 | verifyTestCases({arg: 'arg', type: 'integer', required: true}, [ 23 | // Valid values 24 | ['arg=0', 0], 25 | ['arg=1', 1], 26 | ['arg=-1', -1], 27 | 28 | // Empty values should trigger ERROR_BAD_REQUEST 29 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 30 | ['arg', ERROR_BAD_REQUEST], 31 | ['arg=', ERROR_BAD_REQUEST], 32 | 33 | // Empty-like values should trigger ERROR_BAD_REQUEST too 34 | ['arg=undefined', ERROR_BAD_REQUEST], 35 | ['arg=null', ERROR_BAD_REQUEST], 36 | ]); 37 | }); 38 | 39 | describe(prefix + ' - integer - optional', function() { 40 | // See verifyTestCases' jsdoc for details about the format of test cases. 41 | verifyTestCases({arg: 'arg', type: 'integer'}, [ 42 | // Empty values 43 | [EMPTY_QUERY, undefined], 44 | ['arg', undefined], 45 | ['arg=', undefined], 46 | 47 | // Valid values 48 | ['arg=0', 0], 49 | ['arg=1', 1], 50 | ['arg=-1', -1], 51 | 52 | // Numbers larger than MAX_SAFE_INTEGER should trigger ERROR_BAD_REQUEST 53 | ['arg=2343546576878989879789', ERROR_BAD_REQUEST], 54 | ['arg=-2343546576878989879789', ERROR_BAD_REQUEST], 55 | // Scientific notation 56 | ['arg=1.234e%2B3', 1.234e+3], 57 | ['arg=-1.234e%2B3', -1.234e+3], 58 | 59 | // Invalid values should trigger ERROR_BAD_REQUEST 60 | ['arg=1.2', ERROR_BAD_REQUEST], 61 | ['arg=-1.2', ERROR_BAD_REQUEST], 62 | ['arg=undefined', ERROR_BAD_REQUEST], 63 | ['arg=null', ERROR_BAD_REQUEST], 64 | ['arg=true', ERROR_BAD_REQUEST], 65 | ['arg=false', ERROR_BAD_REQUEST], 66 | ['arg=text', ERROR_BAD_REQUEST], 67 | ['arg=[]', ERROR_BAD_REQUEST], 68 | ['arg=[1,2]', ERROR_BAD_REQUEST], 69 | ['arg={}', ERROR_BAD_REQUEST], 70 | ['arg={"a":true}', ERROR_BAD_REQUEST], 71 | 72 | // Numbers starting with a leading zero are parsed, 73 | // because we know the expected type is a number. 74 | // See https://github.com/strongloop/strong-remoting/issues/143 75 | ['arg=0668', 668], 76 | ['arg=0.42', ERROR_BAD_REQUEST], // not an integer 77 | ]); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-number.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | module.exports = function(ctx) { 11 | suite('query string', urlEncodedContext(ctx, 'qs')); 12 | suite('form data', urlEncodedContext(ctx, 'form')); 13 | }; 14 | 15 | function suite(prefix, ctx) { 16 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 17 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 18 | const verifyTestCases = ctx.verifyTestCases; 19 | 20 | describe(prefix + ' - number - required', function() { 21 | // See verifyTestCases' jsdoc for details about the format of test cases. 22 | verifyTestCases({arg: 'arg', type: 'number', required: true}, [ 23 | // Valid values 24 | ['arg=0', 0], 25 | ['arg=1', 1], 26 | ['arg=-1', -1], 27 | 28 | // Empty values should trigger ERROR_BAD_REQUEST 29 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 30 | ['arg', ERROR_BAD_REQUEST], 31 | ['arg=', ERROR_BAD_REQUEST], 32 | 33 | // Empty-like values should trigger ERROR_BAD_REQUEST too 34 | ['arg=undefined', ERROR_BAD_REQUEST], 35 | ['arg=null', ERROR_BAD_REQUEST], 36 | ]); 37 | }); 38 | 39 | describe(prefix + ' - number - optional', function() { 40 | // See verifyTestCases' jsdoc for details about the format of test cases. 41 | verifyTestCases({arg: 'arg', type: 'number'}, [ 42 | // Empty values 43 | [EMPTY_QUERY, undefined], 44 | ['arg', undefined], 45 | ['arg=', undefined], 46 | 47 | // Valid values 48 | ['arg=0', 0], 49 | ['arg=1', 1], 50 | ['arg=-1', -1], 51 | ['arg=1.2', 1.2], 52 | ['arg=-1.2', -1.2], 53 | // Numbers larger than MAX_SAFE_INTEGER get trimmed 54 | ['arg=2343546576878989879789', 2.34354657687899e+21], 55 | ['arg=-2343546576878989879789', -2.34354657687899e+21], 56 | // Scientific notation 57 | ['arg=1.234e%2B30', 1.234e+30], 58 | ['arg=-1.234e%2B30', -1.234e+30], 59 | 60 | // Invalid values should trigger ERROR_BAD_REQUEST 61 | ['arg=undefined', ERROR_BAD_REQUEST], 62 | ['arg=null', ERROR_BAD_REQUEST], 63 | ['arg=true', ERROR_BAD_REQUEST], 64 | ['arg=false', ERROR_BAD_REQUEST], 65 | ['arg=text', ERROR_BAD_REQUEST], 66 | ['arg=[]', ERROR_BAD_REQUEST], 67 | ['arg=[1,2]', ERROR_BAD_REQUEST], 68 | ['arg={}', ERROR_BAD_REQUEST], 69 | ['arg={"a":true}', ERROR_BAD_REQUEST], 70 | 71 | // Numbers starting with a leading zero are parsed, 72 | // because we know the expected type is a number. 73 | // See https://github.com/strongloop/strong-remoting/issues/143 74 | ['arg=0668', 668], 75 | ['arg=0.42', 0.42], 76 | ]); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /test/rest-coercion/urlencoded-string.suite.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const urlEncodedContext = require('./_urlencoded.context'); 9 | 10 | module.exports = function(ctx) { 11 | suite('query string', urlEncodedContext(ctx, 'qs')); 12 | suite('form data', urlEncodedContext(ctx, 'form')); 13 | }; 14 | 15 | function suite(prefix, ctx) { 16 | const EMPTY_QUERY = ctx.EMPTY_QUERY; 17 | const ERROR_BAD_REQUEST = ctx.ERROR_BAD_REQUEST; 18 | const verifyTestCases = ctx.verifyTestCases; 19 | 20 | describe(prefix + ' - string - required', function() { 21 | // See verifyTestCases' jsdoc for details about the format of test cases. 22 | verifyTestCases({arg: 'arg', type: 'string', required: true}, [ 23 | // Valid values 24 | ['arg=text', 'text'], 25 | // Empty-like values are treated as strings 26 | ['arg=undefined', 'undefined'], 27 | ['arg=null', 'null'], 28 | 29 | // Empty values should trigger ERROR_BAD_REQUEST 30 | [EMPTY_QUERY, ERROR_BAD_REQUEST], 31 | ['arg', ERROR_BAD_REQUEST], 32 | ['arg=', ERROR_BAD_REQUEST], 33 | ]); 34 | }); 35 | 36 | describe(prefix + ' - string - optional', function() { 37 | // See verifyTestCases' jsdoc for details about the format of test cases. 38 | verifyTestCases({arg: 'arg', type: 'string'}, [ 39 | // Empty values 40 | [EMPTY_QUERY, undefined], 41 | ['arg', undefined], 42 | ['arg=', undefined], 43 | 44 | // Valid values - all non-empty value are valid strings 45 | ['arg=undefined', 'undefined'], 46 | ['arg=null', 'null'], 47 | ['arg=0', '0'], 48 | ['arg=1', '1'], 49 | ['arg=false', 'false'], 50 | ['arg=true', 'true'], 51 | ['arg=-1', '-1'], 52 | ['arg=1.2', '1.2'], 53 | ['arg=-1.2', '-1.2'], 54 | ['arg=text', 'text'], 55 | ['arg=[]', '[]'], 56 | ['arg=[1,2]', '[1,2]'], 57 | ['arg={}', '{}'], 58 | ['arg={"a":true}', '{"a":true}'], 59 | // Numbers larger than MAX_SAFE_INTEGER are preserved in string 60 | ['arg=2343546576878989879789', '2343546576878989879789'], 61 | ['arg=-2343546576878989879789', '-2343546576878989879789'], 62 | // Scientific notation 63 | ['arg=1.234e%2B30', '1.234e+30'], 64 | ['arg=-1.234e%2B30', '-1.234e+30'], 65 | 66 | // Numbers starting with a leading zero are treated as strings 67 | // See https://github.com/strongloop/strong-remoting/issues/143 68 | ['arg=0668', '0668'], 69 | ['arg=0.42', '0.42'], 70 | ]); 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /test/type-registry.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: strong-remoting 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | const expect = require('chai').expect; 9 | const TypeRegistry = require('../lib/type-registry'); 10 | 11 | describe('TypeRegistry', function() { 12 | let registry; 13 | beforeEach(function() { 14 | registry = new TypeRegistry(); 15 | }); 16 | 17 | it('refuses to override built-in file type', function() { 18 | expect(function() { 19 | registry.registerType('File', { 20 | fromTypedValue: function() {}, 21 | fromSloppyValue: function() {}, 22 | validate: function() {}, 23 | }); 24 | }).to.throw(/file/); 25 | }); 26 | }); 27 | --------------------------------------------------------------------------------