diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3ee22e5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..962ec7c --- /dev/null +++ b/.eslintrc @@ -0,0 +1,134 @@ +{ + "env": { + "browser": false, + "node": true, + "amd": true + }, + + "rules": { + "no-alert": 2, + "no-array-constructor": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-catch-shadow": 2, + "no-comma-dangle": 2, + "no-cond-assign": 2, + "no-console": 1, + "no-constant-condition": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-div-regex": 1, + "no-dupe-keys": 2, + "no-else-return": 2, + "no-empty": 2, + "no-empty-class": 2, + "no-empty-label": 2, + "no-eq-null": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 1, + "no-extra-semi": 2, + "no-extra-strict": 2, + "no-fallthrough": 2, + "no-floating-decimal": 1, + "no-func-assign": 2, + "no-global-strict": 2, + "no-implied-eval": 2, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-lonely-if": 2, + "no-loop-func": 2, + "no-mixed-requires": [2, false], + "no-multi-str": 2, + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-nested-ternary": 1, + "no-new": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 1, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-path-concat": 0, + "no-plusplus": 2, + "no-process-exit": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-restricted-modules": 0, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 0, + "no-sequences": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-space-before-semi": 2, + "no-sparse-arrays": 2, + "no-sync": 0, + "no-ternary": 2, + "no-trailing-spaces": 2, + "no-undef": 2, + "no-undefined": 1, + "no-undef-init": 2, + "no-underscore-dangle": 0, + "no-unreachable": 2, + "no-unused-expressions": 2, + "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], + "no-use-before-define": 2, + "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], + "no-with": 2, + "no-wrap-func": 2, + "no-mixed-spaces-and-tabs": [2, false], + + "block-scoped-var": 1, + "brace-style": [2, "1tbs"], + "camelcase": 2, + "complexity": [0, 11], + "consistent-return": 2, + "consistent-this": [2, "that"], + "curly": [2, "all"], + "default-case": 1, + "dot-notation": 2, + "eol-last": 2, + "eqeqeq": 2, + "func-names": 0, + "func-style": [2, "declaration"], + "guard-for-in": 1, + "max-depth": [0, 4], + "max-len": [1, 80, 4], + "max-nested-callbacks": [1, 2], + "max-params": [0, 3], + "max-statements": [0, 10], + "handle-callback-err": 1, + "new-cap": 2, + "new-parens": 2, + "one-var": 0, + "quote-props": 1, + "quotes": [2, "single"], + "radix": 2, + "semi": 2, + "sort-vars": 1, + "space-after-keywords": [2, "always"], + "space-in-brackets": [1, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-word-ops": 1, + "strict": 0, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + "wrap-iife": 1, + "wrap-regex": 1, + "yoda": [2, "never"] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57befb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +.idea +.project +*.sublime-* +.DS_Store +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.swp +*.swo +node_modules +coverage +*.tgz +*.xml +*._* diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/.jshintignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..17430da --- /dev/null +++ b/.jshintrc @@ -0,0 +1,21 @@ +{ + "node": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "eqeqeq": true, + "eqnull": true, + "immed": true, + "indent": 2, + "latedef": "nofunc", + "newcap": true, + "nonew": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "trailing": true, + "sub": true, + "maxlen": 80 +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..7ec7473 --- /dev/null +++ b/.npmignore @@ -0,0 +1,16 @@ +.idea +.project +*.sublime-* +.DS_Store +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.swp +*.swo +node_modules +coverage +*.tgz +*.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..80b5737 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,194 @@ + +### Contributing ### + +Thank you for your interest in `loopback-connector-remote`, an open source project +administered by StrongLoop. + +Contributing to loopback is easy. In a few simple steps: + + * Ensure that your effort is aligned with the project’s roadmap by + talking to the maintainers, especially if you are going to spend a + lot of time on it. This project is currently maintained by + [@ritch](https://github.com/ritch), [@raymondfeng](https://github.com/raymondfeng), + and [@bajtos](https://github.com/bajtos). The preferred channel of communication + is [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs) or + [Github Issues](https://github.com/strongloop/loopback/issues). + + * Make something better or fix a bug. + + * Adhere to code style outlined in the + [Google Javascript Style Guide][]. + + * [Sign your patches](#signing-patches) to indicate that your are + making your contribution available under the terms of the + [Contributor License Agreement](#contributor-license-agreement). + + * Submit a pull request through Github. + + +### Signing patches ### + +Like many open source projects, we need a contributor license agreement +from you before we can merge in your changes. + +In summary, by submitting your code, you are granting us a right to use +that code under the terms of this Agreement, including providing it to +others. You are also certifying that you wrote it, and that you are +allowed to license it to us. You are not giving up your copyright in +your work. The license does not change your rights to use your own +contributions for any other purpose. + +Contributor License Agreements are important because they define the +chain of ownership of a piece of software. Some companies won't allow +the use of free software without clear agreements around code ownership. +That's why many open source projects collect similar agreements from +contributors. The CLA here is based on the Apache CLA. + +To signify your agreement to these terms, add the following line to the +bottom of your commit message. Use your real name and an actual e-mail +address. + +``` +Signed-off-by: Random J Developer +``` + +Alternatively you can use the git command line to automatically add this +line, as follows: + +``` +$ git commit -sm "Replace rainbows by unicorns" +``` + + +### Contributor License Agreement ### + +``` + Individual Contributor License Agreement + + By signing this Individual Contributor License Agreement + ("Agreement"), and making a Contribution (as defined below) to + StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and + agree to the following terms and conditions for Your present and + future Contributions submitted to StrongLoop. Except for the license + granted in this Agreement to StrongLoop and recipients of software + distributed by StrongLoop, You reserve all right, title, and interest + in and to Your Contributions. + + 1. Definitions + + "You" or "Your" shall mean the copyright owner or the individual + authorized by the copyright owner that is entering into this + Agreement with StrongLoop. + + "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, that + is intentionally submitted by You to StrongLoop for inclusion in, + or documentation of, any of the products owned or managed by + StrongLoop ("Work"). For purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication + sent to StrongLoop or its representatives, including but not + limited to communication or electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, + or on behalf of, StrongLoop for the purpose of discussing and + improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by You as + "Not a Contribution." + + 2. You Grant a Copyright License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and recipients of software distributed by + StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable copyright license to reproduce, prepare + derivative works of, publicly display, publicly perform, + sublicense, and distribute Your Contributions and such derivative + works under any license and without any restrictions. + + 3. You Grant a Patent License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and to recipients of software distributed by + StrongLoop a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this Section) + patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work under any license and + without any restrictions. The patent license You grant to + StrongLoop under this Section applies only to those patent claims + licensable by You that are necessarily infringed by Your + Contributions(s) alone or by combination of Your Contributions(s) + with the Work to which such Contribution(s) was submitted. If any + entity institutes a patent litigation against You or any other + entity (including a cross-claim or counterclaim in a lawsuit) + alleging that Your Contribution, or the Work to which You have + contributed, constitutes direct or contributory patent + infringement, any patent licenses granted to that entity under + this Agreement for that Contribution or Work shall terminate as + of the date such litigation is filed. + + 4. You Have the Right to Grant Licenses to StrongLoop + + You represent that You are legally entitled to grant the licenses + in this Agreement. + + If Your employer(s) has rights to intellectual property that You + create, You represent that You have received permission to make + the Contributions on behalf of that employer, that Your employer + has waived such rights for Your Contributions, or that Your + employer has executed a separate Corporate Contributor License + Agreement with StrongLoop. + + 5. The Contributions Are Your Original Work + + You represent that each of Your Contributions are Your original + works of authorship (see Section 8 (Submissions on Behalf of + Others) for submission on behalf of others). You represent that to + Your knowledge, no other person claims, or has the right to claim, + any right in any intellectual property right related to Your + Contributions. + + You also represent that You are not legally obligated, whether by + entering into an agreement or otherwise, in any way that conflicts + with the terms of this Agreement. + + You represent that Your Contribution submissions include complete + details of any third-party license or other restriction (including, + but not limited to, related patents and trademarks) of which You + are personally aware and which are associated with any part of + Your Contributions. + + 6. You Don't Have an Obligation to Provide Support for Your Contributions + + You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. + + 6. No Warranties or Conditions + + StrongLoop acknowledges that unless required by applicable law or + agreed to in writing, You provide Your Contributions on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES + OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR + FITNESS FOR A PARTICULAR PURPOSE. + + 7. Submission on Behalf of Others + + If You wish to submit work that is not Your original creation, You + may submit it to StrongLoop separately from any Contribution, + identifying the complete details of its source and of any license + or other restriction (including, but not limited to, related + patents, trademarks, and license agreements) of which You are + personally aware, and conspicuously marking the work as + "Submitted on Behalf of a Third-Party: [named here]". + + 8. Agree to Notify of Change of Circumstances + + You agree to notify StrongLoop of any facts or circumstances of + which You become aware that would make these representations + inaccurate in any respect. Email us at callback@strongloop.com. +``` + + +[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml +[license]: LICENSE + diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..5697bf3 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,50 @@ +/*global module:false*/ +module.exports = function(grunt) { + // Project configuration. + grunt.initConfig({ + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', + // Task configuration. + jshint: { + options: { + jshintrc: true + }, + gruntfile: { + src: 'Gruntfile.js' + }, + lib_test: { + src: ['lib/**/*.js', 'test/**/*.js'] + } + }, + mochaTest: { + 'unit': { + src: 'test/*.js', + options: { + reporter: 'dot' + } + }, + 'unit-xml': { + src: 'test/*.js', + options: { + reporter: 'xunit', + captureFile: 'xunit.xml' + } + } + } + }); + + // These plugins provide necessary tasks. + grunt.loadNpmTasks('grunt-mocha-test'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + + // Default task. + grunt.registerTask('default', ['test']); + + grunt.registerTask('test', [ + process.env.JENKINS_HOME ? 'mochaTest:unit-xml' : 'mochaTest:unit']); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..16bb096 --- /dev/null +++ b/LICENSE @@ -0,0 +1,316 @@ +Copyright (c) 2013-2014 StrongLoop, Inc and other contributors. + +loopback uses a 'dual license' model. Users may use loopback under the terms of +the MIT license, or under the StrongLoop License. The text of both is included +below. + +MIT license + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +StrongLoop License + +You may obtain a copy of the License at + + http://www.strongloop.com/license/ + +STRONGLOOP SUBSCRIPTION AGREEMENT + +PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU +ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE +AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT +AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR +INSTALL OR USE THE SOFTWARE. +This StrongLoop Subscription Agreement ("Agreement") is made by and between +StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S. +B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this +Agreement ("Customer"). The effective date ("Effective Date") of this Agreement +is the date Customer agrees to these terms or installs or uses the Software (as +defined below). This Agreement applies to Customer's use of the Software but it +shall be superseded by any signed agreement between you and StrongLoop +concerning the Software. +1. Subscriptions and Licenses. +1.1 Subscriptions. StrongLoop offers five different subscription levels to its +customers, each as more particularly described on StrongLoop's website located +at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3) +Professional; (4) Gold; and (5) Platinum. The actual subscription level +applicable to Customer (the "Subscription") will be specified in the purchase +order that Customer issues to StrongLoop. This Agreement applies to Customer +regardless of the level of the Subscription selected by Customer and whether or +not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees +to provide the services as described on the StrongLoop Site for each +Subscription level during the term for which Customer has purchased the +applicable Subscription, subject to Customer paying the fees applicable to the +Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may +modify the services to be provided under any Subscription upon notice to +Customer. +1.2 License Grant. Subject to the terms and conditions of this Agreement, +StrongLoop grants to Customer, during the Subscription Term (as defined in +Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive, +non-transferable right and license, to install and use the StrongLoop Suite +software (the "Software") and the documentation made available electronically as +part of the Software (the "Documentation"), either of which may be modified +during the Term (as defined in Section 7.1 below), solely for development, +production and commercial purposes so long as Customer is using the Software to +run only one process on a given operating system at a time. This Agreement, +including but not limited to the license and restrictions contained herein, +apply to Customer regardless of whether Customer accesses the Software via +download from the StrongLoop Site or through a third-party website or service, +even if Customer acquired the Software prior to agreeing to this Agreement. +1.3 License Restrictions. Customer shall not itself, or through any parent, +subsidiary, affiliate, agent or other third party: + 1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer + in whole or in part, any Software or the Documentation to a third party; + or + 1.3.2 decompile, disassemble, translate, reverse engineer or otherwise + attempt to derive source code from the Software, in whole or in part, nor + shall Customer use any mechanical, electronic or other method to trace, + decompile, disassemble, or identify the source code of the Software or + encourage others to do so, except to the limited extent, if any, that + applicable law permits such acts notwithstanding any contractual + prohibitions, provided, however, before Customer exercises any rights that + Customer believes to be entitled to based on mandatory law, Customer shall + provide StrongLoop with thirty (30) days prior written notice and provide + all reasonably requested information to allow StrongLoop to assess + Customer's claim and, at StrongLoop's sole discretion, to provide + alternatives that reduce any adverse impact on StrongLoop's intellectual + property or other rights; or + 1.3.3 allow access or permit use of the Software by any users other than + Customer's employees or authorized third-party contractors who are + providing services to Customer and agree in writing to abide by the terms + of this Agreement, provided further that Customer shall be liable for any + failure by such employees and third-party contractors to comply with the + terms of this Agreement and no usage restrictions, if any, shall be + exceeded; or + 1.3.4 create, develop, license, install, use, or deploy any third party + software or services to circumvent or provide access, permissions or + rights which violate the license keys embedded within the Software; or + 1.3.5 modify or create derivative works based upon the Software or + Documentation; or disclose the results of any benchmark test of the + Software to any third party without StrongLoop's prior written approval; + or + 1.3.6 change any proprietary rights notices which appear in the Software + or Documentation; or + 1.3.7 use the Software as part of a time sharing or service bureau + purposes or in any other resale capacity. +1.4 Third-Party Software. The Software may include individual certain software +that is owned by third parties, including individual open source software +components (the "Third-Party Software"), each of which has its own copyright and +its own applicable license conditions. Such third-party software is licensed to +Customer under the terms of the applicable third-party licenses and/or copyright +notices that can be found in the LICENSES file, the Documentation or other +materials accompanying the Software, except that Sections 5 (Warranty +Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the +third-party software. Customer agrees to comply with the terms and conditions +of the relevant third-party software licenses. +2. Support Services. StrongLoop has no obligation to provide any support for +the Software other than the support services specifically described on the +StrongLoop Site for the Subscription level procured by Customer. However, +StrongLoop has endeavored to establish a community of users of the Software who +have provided their own feedback, hints and advice regarding their experiences +in using the Software. You can find that community and user feedback on the +StrongLoop Site. The use of any information, content or other materials from, +contained in or on the StrongLoop Site are subject to the StrongLoop website +terms of use located here http://www.strongloop.com/terms-of-service. +3. Confidentiality. For purposes of this Agreement, "Confidential Information" +means any and all information or proprietary materials (in every form and media) +not generally known in the relevant trade or industry and which has been or is +hereafter disclosed or made available by StrongLoop to Customer in connection +with the transactions contemplated under this Agreement, including (i) all trade +secrets, (ii) existing or contemplated Software, services, designs, technology, +processes, technical data, engineering, techniques, methodologies and concepts +and any related information, and (iii) information relating to business plans, +sales or marketing methods and customer lists or requirements. For a period of +five (5) years from the date of disclosure of the applicable Confidential +Information, Customer shall (i) hold the Confidential Information in trust and +confidence and avoid the disclosure or release thereof to any other person or +entity by using the same degree of care as it uses to avoid unauthorized use, +disclosure, or dissemination of its own Confidential Information of a similar +nature, but not less than reasonable care, and (ii) not use the Confidential +Information for any purpose whatsoever except as expressly contemplated under +this Agreement; provided that, to the extent the Confidential Information +constitutes a trade secret under law, Customer agrees to protect such +information for so long as it qualifies as a trade secret under applicable law. +Customer shall disclose the Confidential Information only to those of its +employees and contractors having a need to know such Confidential Information +and shall take all reasonable precautions to ensure that such employees and +contractors comply with the provisions of this Section. The obligations of +Customer under this Section shall not apply to information that Customer can +demonstrate (i) was in its possession at the time of disclosure and without +restriction as to confidentiality, (ii) at the time of disclosure is generally +available to the public or after disclosure becomes generally available to the +public through no breach of agreement or other wrongful act by Customer, (iii) +has been received from a third party without restriction on disclosure and +without breach of agreement by Customer, or (iv) is independently developed by +Customer without regard to the Confidential Information. In addition, Customer +may disclose Confidential Information as required to comply with binding orders +of governmental entities that have jurisdiction over it; provided that Customer +gives StrongLoop reasonable written notice to allow StrongLoop to seek a +protective order or other appropriate remedy, discloses only such Confidential +Information as is required by the governmental entity, and uses commercially +reasonable efforts to obtain confidential treatment for any Confidential +Information disclosed. Notwithstanding the above, Customer agrees that +StrongLoop, its employees and agents shall be free to use and employ their +general skills, know-how, and expertise, and to use, disclose, and employ any +generalized ideas, concepts, know-how, methods, techniques or skills gained or +learned during the Term or thereafter. +4. Ownership. StrongLoop shall retain all intellectual property and proprietary +rights in the Software, Documentation, and related works, including but not +limited to any derivative work of the foregoing and StrongLoop's licensors shall +retain all intellectual property and proprietary rights in any Third-Party +Software that may be provided with or as a part of the Software. Customer shall +do nothing inconsistent with StrongLoop's or its licensors' title to the +Software and the intellectual property rights embodied therein, including, but +not limited to, transferring, loaning, selling, assigning, pledging, or +otherwise disposing, encumbering, or suffering a lien or encumbrance upon or +against any interest in the Software. The Software (including any Third-Party +Software) contain copyrighted material, trade secrets and other proprietary +material of StrongLoop and/or its licensors. +5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND +DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP, +ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE, +PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE, +COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES +NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR +ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE +WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN +INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES +SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. +STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE, +BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW +THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO +CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE +SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN +THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR +FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS +SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS. +6. Limitation of Liability. + 6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS + BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE, + CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND + HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND + THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND + THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE + SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE + AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF + SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL + STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF + ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE) + ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE + HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER + TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM + ARISES. + 6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE + ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT. + THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT + BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING + LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT + PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL + PURPOSE. + 6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE + LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY + SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. + 6.4 Allocation of Risk. The sections on limitation of liability and + disclaimer of warranties allocate the risks in the Agreement between the + parties. This allocation is an essential element of the basis of the + bargain between the parties. +7. Term and Termination. +7.1 This Agreement shall commence on the Effective Date and continue for so long +as Customer has a valid Subscription and is current on the payment of any +Subscription Fees required to be paid for that Subscription (the "Subscription +Term"). Either party may terminate this Agreement immediately upon written +notice to the other party, and the Subscription and licenses granted hereunder +automatically terminate upon the termination of this Agreement. This Agreement +will terminate immediately without notice from StrongLoop if Customer fails to +comply with or otherwise breaches any provision of this Agreement. +7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall +survive the expiration or termination of this Agreement. +8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay +StrongLoop the Subscription Fees as described on the StrongLoop Site for the +Subscription purchased unless a different amount has been agreed to in a +separate agreement between Customer and StrongLoop. In addition, Customer shall +pay all sales, use, value added, withholding, excise taxes and other tax, duty, +custom and similar fees levied upon the delivery or use of the Software and the +Subscriptions described in this Agreement. Fees shall be invoiced in full upon +StrongLoop's acceptance of Customer's purchase order for the Subscription. All +invoices shall be paid in US dollars and are due upon receipt and shall be paid +within thirty (30) days. Payments shall be made without right of set-off or +chargeback. If Customer does not pay the invoices when due, StrongLoop may +charge interest at one percent (1%) per month or the highest rate permitted by +law, whichever is lower, on the unpaid balance from the original due date. If +Customer fails to pay fees in accordance with this Section, StrongLoop may +suspend fulfilling its obligations under this Agreement (including but not +limited to suspending the services under the Subscription) until payment is +received by StrongLoop. If any applicable law requires Customer to withhold +amounts from any payments to StrongLoop under this Agreement, (a) Customer shall +effect such withholding, remit such amounts to the appropriate taxing +authorities and promptly furnish StrongLoop with tax receipts evidencing the +payments of such amounts and (b) the sum payable by Customer upon which the +deduction or withholding is based shall be increased to the extent necessary to +ensure that, after such deduction or withholding, StrongLoop receives and +retains, free from liability for such deduction or withholding, a net amount +equal to the amount StrongLoop would have received and retained absent the +required deduction or withholding. +9. General. +9.1 Compliance with Laws. Customer shall abide by all local, state, federal and +international laws, rules, regulations and orders applying to Customer's use of +the Software, including, without limitation, the laws and regulations of the +United States that may restrict the export and re-export of certain commodities +and technical data of United States origin, including the Software. Customer +agrees that it will not export or re-export the Software without the appropriate +United States or foreign government licenses. +9.2 Entire Agreement. This Agreement constitutes the entire agreement between +the parties concerning the subject matter hereof. This Agreement supersedes all +prior or contemporaneous discussions, proposals and agreements between the +parties relating to the subject matter hereof. No amendment, modification or +waiver of any provision of this Agreement shall be effective unless in writing +and signed by both parties. Any additional or different terms on any purchase +orders issued by Customer to StrongLoop shall not be binding on either party, +are hereby rejected by StrongLoop and void. +9.3 Severability. If any provision of this Agreement is held to be invalid or +unenforceable, the remaining portions shall remain in full force and effect and +such provision shall be enforced to the maximum extent possible so as to effect +the intent of the parties and shall be reformed to the extent necessary to make +such provision valid and enforceable. +9.4 Waiver. No waiver of rights by either party may be implied from any actions +or failures to enforce rights under this Agreement. +9.5 Force Majeure. Neither party shall be liable to the other for any delay or +failure to perform due to causes beyond its reasonable control (excluding +payment of monies due). +9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the +terms of this Agreement are intended to be and are solely for the benefit of +StrongLoop and Customer and do not create any right in favor of any third party. +9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the +laws of the State of California, without reference to the principles of +conflicts of law. The provisions of the Uniform Computerized Information +Transaction Act and United Nations Convention on Contracts for the International +Sale of Goods shall not apply to this Agreement. The parties shall attempt to +resolve any dispute related to this Agreement informally, initially through +their respective management, and then by non-binding mediation in San Francisco +County, California. Any litigation related to this Agreement shall be brought +in the state or federal courts located in San Francisco County, California, and +only in those courts and each party irrevocably waives any objections to such +venue. +9.8 Notices. All notices must be in writing and shall be effective three (3) +days after the date sent to the other party's headquarters, Attention Chief +Financial Officer. diff --git a/README.md b/README.md new file mode 100644 index 0000000..77b2ab1 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +## loopback-connector-remote + +Remote REST API connector for loopback-datasource-juggler. + +Please see the full documentation at [docs.strongloop.com](http://docs.strongloop.com/display/LB/REST+connector). + +## Running tests + + npm test \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..191d30b --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/remote-connector'); \ No newline at end of file diff --git a/lib/remote-connector.js b/lib/remote-connector.js new file mode 100644 index 0000000..26beb4a --- /dev/null +++ b/lib/remote-connector.js @@ -0,0 +1,90 @@ +/** + * Dependencies. + */ + +var assert = require('assert'); +var remoting = require('strong-remoting'); +var DataAccessObject = require('loopback-datasource-juggler/lib/dao'); + +/** + * Export the RemoteConnector class. + */ + +module.exports = RemoteConnector; + +/** + * Create an instance of the connector with the given `settings`. + */ + +function RemoteConnector(settings) { + assert(typeof settings === 'object', 'cannot initiaze RemoteConnector without a settings object'); + this.client = settings.client; + this.adapter = settings.adapter || 'rest'; + this.protocol = settings.protocol || 'http' + this.root = settings.root || ''; + this.host = settings.host || 'localhost'; + this.port = settings.port || 3000; + this.remotes = remoting.create(); + + // TODO(ritch) make sure this name works with Model.getSourceId() + this.name = 'remote-connector'; + + if(settings.url) { + this.url = settings.url; + } else { + this.url = this.protocol + '://' + this.host + ':' + this.port + this.root; + } + + // handle mixins in the define() method + var DAO = this.DataAccessObject = function() {}; +} + +RemoteConnector.prototype.connect = function() { + this.remotes.connect(this.url, this.adapter); +} + +RemoteConnector.initialize = function(dataSource, callback) { + var connector = dataSource.connector = new RemoteConnector(dataSource.settings); + connector.connect(); + callback(); +} + +RemoteConnector.prototype.define = function(definition) { + var Model = definition.model; + var remotes = this.remotes; + var SharedClass; + + assert(Model.sharedClass, 'cannot attach ' + Model.modelName + + ' to a remote connector without a Model.sharedClass'); + + remotes.addClass(Model.sharedClass); + + Model + .sharedClass + .methods() + .forEach(function(remoteMethod) { + // TODO(ritch) more elegant way of ignoring a nested shared class + if(remoteMethod.name !== 'Change' + && remoteMethod.name !== 'Checkpoint') { + createProxyMethod(Model, remotes, remoteMethod); + } + }); +} + +function createProxyMethod(Model, remotes, remoteMethod) { + var scope = remoteMethod.isStatic ? Model : Model.prototype; + var original = scope[remoteMethod.name]; + + scope[remoteMethod.name] = function remoteMethodProxy() { + var args = Array.prototype.slice.call(arguments); + var lastArgIsFunc = typeof args[args.length - 1] === 'function'; + var callback; + if(lastArgIsFunc) { + callback = args.pop(); + } + + remotes.invoke(remoteMethod.stringName, args, callback); + } +} + +function noop() {} diff --git a/package.json b/package.json new file mode 100644 index 0000000..938b665 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "loopback-connector-remote", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "grunt test" + }, + "repository": { + "type": "git", + "url": "https://github.com/kraman/loopback-connector-remotekr.git" + }, + "author": "Krishna Raman ", + "license": "StrongLoop", + "bugs": { + "url": "https://github.com/kraman/loopback-connector-remotekr/issues" + }, + "homepage": "https://github.com/kraman/loopback-connector-remotekr", + "dependencies": { + "loopback-datasource-juggler": "^2.8.0", + "strong-remoting": "^2.1.0" + }, + "devDependencies": { + "assert": "^1.1.2", + "async": "^0.9.0", + "grunt": "~0.4.5", + "grunt-browserify": "~3.0.1", + "grunt-cli": "^0.1.13", + "grunt-contrib-jshint": "~0.10.0", + "grunt-karma": "~0.9.0", + "grunt-mocha-test": "^0.11.0", + "karma": "~0.12.23", + "karma-junit-reporter": "^0.2.2", + "karma-mocha": "^0.1.9", + "loopback": "^2.2.0", + "loopback-datasource-juggler": "^2.9.0", + "mocha": "~1.21.4", + "strong-task-emitter": "0.0.5", + "supertest": "~0.13.0" + } +} diff --git a/test/remote-connector.test.js b/test/remote-connector.test.js new file mode 100644 index 0000000..6c95b46 --- /dev/null +++ b/test/remote-connector.test.js @@ -0,0 +1,72 @@ +var loopback = require('loopback'); +var defineModelTestsWithDataSource = require('./util/model-tests'); +var assert = require('assert'); + +describe('RemoteConnector', function() { + var remoteApp; + var remote; + + defineModelTestsWithDataSource({ + beforeEach: function(done) { + var test = this; + remoteApp = loopback(); + remoteApp.use(loopback.rest()); + remoteApp.listen(0, function() { + test.dataSource = loopback.createDataSource({ + host: remoteApp.get('host'), + port: remoteApp.get('port'), + connector: loopback.Remote + }); + done(); + }); + }, + onDefine: function(Model) { + var RemoteModel = Model.extend(Model.modelName); + RemoteModel.attachTo(loopback.createDataSource({ + connector: loopback.Memory + })); + remoteApp.model(RemoteModel); + } + }); + + beforeEach(function(done) { + var test = this; + remoteApp = this.remoteApp = loopback(); + remoteApp.use(loopback.rest()); + var ServerModel = this.ServerModel = loopback.PersistedModel.extend('TestModel'); + + remoteApp.model(ServerModel); + + remoteApp.listen(0, function() { + test.remote = loopback.createDataSource({ + host: remoteApp.get('host'), + port: remoteApp.get('port'), + connector: loopback.Remote + }); + done(); + }); + }); + + it('should support the save method', function (done) { + var calledServerCreate = false; + var RemoteModel = loopback.PersistedModel.extend('TestModel'); + RemoteModel.attachTo(this.remote); + + var ServerModel = this.ServerModel; + + ServerModel.create = function(data, cb) { + calledServerCreate = true; + data.id = 1; + cb(null, data); + } + + ServerModel.setupRemoting(); + + var m = new RemoteModel({foo: 'bar'}); + m.save(function(err, inst) { + assert(inst instanceof RemoteModel); + assert(calledServerCreate); + done(); + }); + }); +}); diff --git a/test/util/describe.js b/test/util/describe.js new file mode 100644 index 0000000..ff34ec2 --- /dev/null +++ b/test/util/describe.js @@ -0,0 +1,19 @@ +var loopback = require('loopback'); + +module.exports = describe; + +describe.onServer = function describeOnServer(name, fn) { + if (loopback.isServer) { + describe(name, fn); + } else { + describe.skip(name, fn); + } +}; + +describe.inBrowser = function describeInBrowser(name, fn) { + if (loopback.isBrowser) { + describe(name, fn); + } else { + describe.skip(name, fn); + } +}; diff --git a/test/util/it.js b/test/util/it.js new file mode 100644 index 0000000..a2f23b7 --- /dev/null +++ b/test/util/it.js @@ -0,0 +1,19 @@ +var loopback = require('loopback'); + +module.exports = it; + +it.onServer = function itOnServer(name, fn) { + if (loopback.isServer) { + it(name, fn); + } else { + it.skip(name, fn); + } +}; + +it.inBrowser = function itInBrowser(name, fn) { + if (loopback.isBrowser) { + it(name, fn); + } else { + it.skip(name, fn); + } +}; diff --git a/test/util/model-tests.js b/test/util/model-tests.js new file mode 100644 index 0000000..16b1e31 --- /dev/null +++ b/test/util/model-tests.js @@ -0,0 +1,249 @@ +var assert = require('assert'); +var async = require('async'); +var describe = require('./describe'); +var loopback = require('loopback'); +var ACL = loopback.ACL; +var Change = loopback.Change; +var PersistedModel = loopback.PersistedModel; +var RemoteObjects = require('strong-remoting'); +var TaskEmitter = require('strong-task-emitter'); + +module.exports = function defineModelTestsWithDataSource(options) { + +describe('Model Tests', function() { + + var User, dataSource; + + if(options.beforeEach) { + beforeEach(options.beforeEach); + } + + beforeEach(function() { + var test = this; + + // setup a model / datasource + dataSource = this.dataSource || loopback.createDataSource(options.dataSource); + + var extend = PersistedModel.extend; + + // create model hook + PersistedModel.extend = function() { + var extendedModel = extend.apply(PersistedModel, arguments); + + if(options.onDefine) { + options.onDefine.call(test, extendedModel); + } + + return extendedModel; + } + + User = PersistedModel.extend('user', { + 'first': String, + 'last': String, + 'age': Number, + 'password': String, + 'gender': String, + 'domain': String, + 'email': String + }, { + trackChanges: true + }); + + // enable destroy all for testing + User.destroyAll.shared = true; + User.attachTo(dataSource); + }); + + describe('Model.validatesPresenceOf(properties...)', function() { + it("Require a model to include a property to be considered valid", function() { + User.validatesPresenceOf('first', 'last', 'age'); + var joe = new User({first: 'joe'}); + assert(joe.isValid() === false, 'model should not validate'); + assert(joe.errors.last, 'should have a missing last error'); + assert(joe.errors.age, 'should have a missing age error'); + }); + }); + + describe('Model.validatesLengthOf(property, options)', function() { + it("Require a property length to be within a specified range", function() { + User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}}); + var joe = new User({password: '1234'}); + assert(joe.isValid() === false, 'model should not be valid'); + assert(joe.errors.password, 'should have password error'); + }); + }); + + describe('Model.validatesInclusionOf(property, options)', function() { + it("Require a value for `property` to be in the specified array", function() { + User.validatesInclusionOf('gender', {in: ['male', 'female']}); + var foo = new User({gender: 'bar'}); + assert(foo.isValid() === false, 'model should not be valid'); + assert(foo.errors.gender, 'should have gender error'); + }); + }); + + describe('Model.validatesExclusionOf(property, options)', function() { + it("Require a value for `property` to not exist in the specified array", function() { + User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']}); + var foo = new User({domain: 'www'}); + var bar = new User({domain: 'billing'}); + var bat = new User({domain: 'admin'}); + assert(foo.isValid() === false); + assert(bar.isValid() === false); + assert(bat.isValid() === false); + assert(foo.errors.domain, 'model should have a domain error'); + assert(bat.errors.domain, 'model should have a domain error'); + assert(bat.errors.domain, 'model should have a domain error'); + }); + }); + + describe('Model.validatesNumericalityOf(property, options)', function() { + it("Require a value for `property` to be a specific type of `Number`", function() { + User.validatesNumericalityOf('age', {int: true}); + var joe = new User({age: 10.2}); + assert(joe.isValid() === false); + var bob = new User({age: 0}); + assert(bob.isValid() === true); + assert(joe.errors.age, 'model should have an age error'); + }); + }); + + describe('myModel.isValid()', function() { + it("Validate the model instance", function() { + User.validatesNumericalityOf('age', {int: true}); + var user = new User({first: 'joe', age: 'flarg'}) + var valid = user.isValid(); + assert(valid === false); + assert(user.errors.age, 'model should have age error'); + }); + + it('Asynchronously validate the model', function(done) { + User.validatesNumericalityOf('age', {int: true}); + var user = new User({first: 'joe', age: 'flarg'}); + user.isValid(function (valid) { + assert(valid === false); + assert(user.errors.age, 'model should have age error'); + done(); + }); + }); + }); + + describe('Model.create([data], [callback])', function() { + it("Create an instance of Model with given data and save to the attached data source", function(done) { + User.create({first: 'Joe', last: 'Bob'}, function(err, user) { + assert(user instanceof User); + done(); + }); + }); + }); + + describe('model.save([options], [callback])', function() { + it("Save an instance of a Model to the attached data source", function(done) { + var joe = new User({first: 'Joe', last: 'Bob'}); + joe.save(function(err, user) { + assert(user.id); + assert(!err); + assert(!user.errors); + done(); + }); + }); + }); + + describe('model.updateAttributes(data, [callback])', function() { + it("Save specified attributes to the attached data source", function(done) { + User.create({first: 'joe', age: 100}, function (err, user) { + assert(!err); + assert.equal(user.first, 'joe'); + + user.updateAttributes({ + first: 'updatedFirst', + last: 'updatedLast' + }, function (err, updatedUser) { + assert(!err); + assert.equal(updatedUser.first, 'updatedFirst'); + assert.equal(updatedUser.last, 'updatedLast'); + assert.equal(updatedUser.age, 100); + done(); + }); + }); + }); + }); + + describe('Model.upsert(data, callback)', function() { + it("Update when record with id=data.id found, insert otherwise", function(done) { + User.upsert({first: 'joe', id: 7}, function (err, user) { + assert(!err); + assert.equal(user.first, 'joe'); + + User.upsert({first: 'bob', id: 7}, function (err, updatedUser) { + assert(!err); + assert.equal(updatedUser.first, 'bob'); + done(); + }); + }); + }); + }); + + describe('model.destroy([callback])', function() { + it("Remove a model from the attached data source", function(done) { + User.create({first: 'joe', last: 'bob'}, function (err, user) { + User.findById(user.id, function (err, foundUser) { + assert.equal(user.id, foundUser.id); + foundUser.destroy(function () { + User.findById(user.id, function (err, notFound) { + assert.equal(notFound, null); + done(); + }); + }); + }); + }); + }); + }); + + describe('Model.deleteById(id, [callback])', function () { + it("Delete a model instance from the attached data source", function (done) { + User.create({first: 'joe', last: 'bob'}, function (err, user) { + User.deleteById(user.id, function (err) { + User.findById(user.id, function (err, notFound) { + assert.equal(notFound, null); + done(); + }); + }); + }); + }); + }); + + describe('Model.findById(id, callback)', function() { + it("Find an instance by id", function(done) { + User.create({first: 'michael', last: 'jordan', id: 23}, function () { + User.findById(23, function (err, user) { + assert.equal(user.id, 23); + assert.equal(user.first, 'michael'); + assert.equal(user.last, 'jordan'); + done(); + }); + }); + }); + }); + + describe('Model.count([query], callback)', function() { + it("Query count of Model instances in data source", function(done) { + (new TaskEmitter()) + .task(User, 'create', {first: 'jill', age: 100}) + .task(User, 'create', {first: 'bob', age: 200}) + .task(User, 'create', {first: 'jan'}) + .task(User, 'create', {first: 'sam'}) + .task(User, 'create', {first: 'suzy'}) + .on('done', function () { + User.count({age: {gt: 99}}, function (err, count) { + assert.equal(count, 2); + done(); + }); + }); + }); + }); + +}); + + +}