├── .babelrc ├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .mailmap ├── .nvmrc ├── AUTHORS ├── CHANGELOG.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── bower.json ├── build_examples ├── browserify │ ├── README.md │ ├── app.js │ └── index.html ├── node │ └── app.js ├── r.js │ ├── README.md │ ├── app.js │ ├── index.html │ ├── main.js │ └── require.config.js ├── webpack │ ├── README.md │ ├── app.js │ ├── index.html │ └── webpack.config.js └── webpack_es6 │ ├── README.md │ ├── app.js │ ├── index.html │ └── webpack.config.js ├── circle.yml ├── conf.json ├── dist └── js-data-http.d.ts ├── fetch ├── .gitignore ├── LICENSE ├── dist │ └── js-data-fetch.d.ts ├── karma.conf.js ├── karma.start.js ├── package.json ├── rollup.config.js └── typings.json ├── karma.conf.js ├── karma.start.js ├── node ├── .gitignore ├── LICENSE ├── dist │ └── js-data-http-node.d.ts ├── mocha.start.js ├── package.json ├── rollup.config.js └── typings.json ├── package-lock.json ├── package.json ├── rollup.config.js ├── scripts └── banner.js ├── src └── index.js ├── test ├── DEL.test.js ├── GET.test.js ├── HTTP.test.js ├── POST.test.js ├── PUT.test.js ├── count.test.js ├── create.test.js ├── createMany.test.js ├── deserialize.test.js ├── destroy.test.js ├── destroyAll.test.js ├── fetch.test.js ├── find.test.js ├── findAll.test.js ├── getEndpoint.test.js ├── getPath.test.js ├── queryTransform.test.js ├── responseError.test.js ├── serialize.test.js ├── static.addAction.test.js ├── static.addActions.test.js ├── sum.test.js ├── update.test.js ├── updateAll.test.js └── updateMany.test.js ├── typings.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to js-data core 2 | 3 | [Read the general Contributing Guide](http://js-data.io/docs/contributing). 4 | 5 | ## Project structure 6 | 7 | * `dist/` - Contains final build files for distribution (js-data-http) 8 | * `doc/` - Output folder for JSDocs 9 | * `fetch/` - Contains js-data-fetch package files 10 | * `dist/` - Contains final build files for distribution (js-data-fetch) 11 | * `node/` - Contains js-data-http-node package files 12 | * `dist/` - Contains final build files for distribution (js-data-http-node) 13 | * `scripts/ - Various build scripts 14 | * `src/` - Project source code 15 | * `test/` - Project tests 16 | 17 | ## Clone, build & test 18 | 19 | 1. `clone git@github.com:js-data/js-data-http.git` 20 | 1. `cd js-data-http` 21 | 1. `npm install` 22 | 1. `npm test` - Build and test 23 | 24 | ## To cut a release 25 | 26 | 1. Checkout master 27 | 1. Bump version in `package.json` appropriately 28 | 1. Update `CHANGELOG.md` appropriately 29 | 1. Run `npm run release` 30 | 1. Commit and push changes 31 | 1. Checkout `release`, merge `master` into `release` 32 | 1. Run `npm run release` again 33 | 1. Commit and push changes 34 | 1. Make a GitHub release 35 | - tag from `release` branch 36 | - set tag name to version 37 | - set release name to version 38 | - set release body to changelog entry for the version 39 | - upload the files in the `dist/` folder 40 | 1. `npm publish .` 41 | 1. checkout `master` 42 | 43 | See also [Community & Support](http://js-data.io/docs/community). 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | (delete this line) GitHub Issues are not for support questions. 2 | (delete this line) GitHub Issues are for bug reports, feature requests, and other issues. 3 | (delete this line) Find out how to Get Support here: http://js-data.io/docs/support. 4 | 5 | 6 | 7 | Thanks! 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # (it's a good idea to open an issue first for discussion) 2 | 3 | - [ ] - `npm test` succeeds 4 | - [ ] - Pull request has been squashed into 1 commit 5 | - [ ] - I did NOT commit changes to `dist/` 6 | - [ ] - Code coverage does not decrease (if any source code was changed) 7 | - [ ] - Appropriate JSDoc comments were updated in source code (if applicable) 8 | - [ ] - Approprate changes to js-data.io docs have been suggested ("Suggest Edits" button) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.map 3 | bower_components/ 4 | 5 | .idea/ 6 | 7 | *.iml 8 | 9 | coverage/ 10 | junit/ 11 | doc/ 12 | node_modules/ 13 | *.log 14 | typings 15 | .nyc_output -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | InternalFX Bryan 2 | Jason Dobry Jason Dobry 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of js-data-http project authors. 2 | # 3 | # Names are formatted as: 4 | # Name or Organization 5 | # 6 | # The email address is not required for organizations. 7 | # 8 | Alex 9 | InternalFX 10 | Ivan Voznyakovsky 11 | Jason Dobry 12 | Josh Drake 13 | nlac 14 | RobertHerhold 15 | Tomás Fox 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ##### 3.0.0 - 02 July 2017 2 | 3 | **v3 stable release** 4 | 5 | ###### Bug fixes 6 | - #65 - axios as devDependency 7 | - #68 - Impossible to set timeout globally 8 | 9 | ##### 3.0.0-rc.3 - 18 August 2016 10 | 11 | ###### Backwards compatible changes 12 | - #56 - Test Coverage (fetch, updateMany/createMany, addAction(s)) by @pik 13 | 14 | ##### 3.0.0-rc.2 - 13 August 2016 15 | 16 | ###### Backwards compatible changes 17 | - Upgraded dependencies 18 | - Finish JSDoc comments 19 | 20 | ###### Bug fixes 21 | - #55 - dist/js-data-http.d.ts is not complete 22 | 23 | ##### 3.0.0-rc.1 - 10 August 2016 24 | 25 | ###### Breaking changes 26 | - Now depending on js-data 3.0.0-rc.4 or greater 27 | 28 | ###### Backwards compatible changes 29 | - Upgraded dependencies 30 | 31 | ##### 3.0.0-beta.8 - 08 July 2016 32 | 33 | ###### Backwards compatible changes 34 | - Upgraded dependencies 35 | - Adapter now extends `Component` 36 | 37 | ##### 3.0.0-beta.7 - 25 May 2016 38 | 39 | ###### Backwards compatible changes 40 | - Upgraded to js-data-adapter 0.7.3 41 | 42 | ##### 3.0.0-beta.6 - 17 May 2016 43 | 44 | ###### Other 45 | - Switch from webpack to rollup 46 | - Upgraded dependencies 47 | 48 | ##### 3.0.0-beta.5 - 02 May 2016 49 | 50 | ###### Bug fixes 51 | - Fixed typos in the code for `addAction` and `addActions` 52 | 53 | ##### 3.0.0-beta.4 - 01 May 2016 54 | 55 | ###### Other 56 | - Couple of JSDoc fixes 57 | - Upgraded dependencies 58 | 59 | ##### 3.0.0-beta.3 - 28 April 2016 60 | 61 | ###### Breaking changes 62 | - js-data-http/js-data-fetch/js-data-http-node no longer export a default module, instead you must do: 63 | - `import {HttpAdapter} from 'js-data-http' 64 | - `var HttpAdapter = require('js-data-http').HttpAdapter 65 | - `var adapter = new window.JSDataHttp.HttpAdapter()` 66 | - `define(['js-data-http'], function (JSDataHttp) { var adapter = new window.JSDataHttp.HttpAdapter(); }) 67 | - Removed `dist/` files from the `master` branch to a `release` branch to cut down on noise 68 | 69 | ###### Backwards compatible changes 70 | - Added `typings` field to `package.json` 71 | - Added `typings.json` 72 | - Added `dist/js-data-http.d.ts` 73 | 74 | ##### 3.0.0-beta.2 - 17 April 2016 75 | 76 | Official v3 beta release 77 | 78 | ###### Other 79 | - Upgraded some devDependencies 80 | 81 | ##### 3.0.0-alpha.10 - 02 April 2016 82 | 83 | ###### Backwards compatible bug fixes 84 | - Fixed double deserialization of data in some cases 85 | 86 | ##### 3.0.0-alpha.9 - 18 March 2016 87 | 88 | ###### Backwards compatible API changes 89 | - Added sum and count methods 90 | 91 | ###### Other 92 | - Cleaned up some things 93 | 94 | ##### 3.0.0-alpha.8 - 17 March 2016 95 | 96 | - Rebased master 97 | 98 | ##### 2.2.1 - 17 March 2016 99 | 100 | ###### Backwards compatible bug fixes 101 | - #44 - Angular/HTTP: Canceling requests with timeout promise does not work 102 | - #45 - Map file path in js-data-http.min.js is incorrect. 103 | - #46 - Sending large payloads takes huge amount of time - thanks @ivanvoznyakovsky 104 | - #47 - perf(http): speed up preparation of http config with large payload - thanks @ivanvoznyakovsky 105 | 106 | ##### 3.0.0-alpha.7 - 10 March 2016 107 | 108 | ###### Other 109 | - Now using js-data-repo-tools 110 | - Now using js-data-adapter 111 | 112 | ##### 3.0.0-alpha.6 - 23 February 2016 113 | 114 | - getEndpoint now works with multiple parents 115 | 116 | ##### 3.0.0-alpha.5 - 23 February 2016 117 | 118 | - Rebased master 119 | 120 | ##### 2.2.0 - 23 February 2016 121 | 122 | ###### Backwards compatible API changes 123 | - #40 - Add support for multiple parents by @tfoxy 124 | 125 | ###### Other 126 | - Upgraded dependencies, including bundled axios 127 | - Added AUTHORS and CONTRIBUTORS files 128 | - Updated Readme 129 | 130 | ##### 3.0.0-alpha.4 - 12 February 2016 131 | 132 | ###### Backwards compatible API changes 133 | - Better debugging/logging 134 | - Updates for the newest js-data alpha 135 | 136 | ##### 3.0.0-alpha.3 - 10 January 2016 137 | 138 | ###### Backwards compatible API changes 139 | - Added updateMany, createMany, and responseError methods. 140 | 141 | ##### 3.0.0-alpha.2 - 09 January 2016 142 | 143 | ###### Breaking API changes 144 | - All options that could be found at `DSHttpAdapter#defaults` will now be on 145 | the actual instances of `DSHttpAdapter`. e.g. `DSHttpAdapter#defaults.deserialize` 146 | is now at `DSHttpAdapter#deserialize`. This makes it easier to extend the 147 | `DSHttpAdapter` class and override its methods. 148 | 149 | ###### Backwards compatible API changes 150 | - Added lifecycle methods: beforeFind, afterPOST, etc. 151 | - Added support for the `raw` option 152 | 153 | ##### 3.0.0-alpha.1 - 12 December 2015 154 | 155 | ###### Breaking API changes 156 | - Actions are now part of js-data-http, rather than js-data 157 | - Now requires js-data 3.x or greater 158 | 159 | ###### Backwards compatible API changes 160 | - Added option to support use of `window.fetch` 161 | - Added option to supply custom http implementation 162 | 163 | ###### Other 164 | - Published the js-data-http-node package, a build of js-data-http that works in Node.js 165 | 166 | ##### 2.1.2 - 28 October 2015 167 | 168 | ###### Backwards compatible API changes 169 | - Added option to support use of `window.fetch` 170 | - Added option to supply custom http implementation 171 | 172 | ###### Other 173 | - Dropped Grunt 174 | - Now reporting code coverage properly 175 | 176 | ##### 2.1.1 - 20 September 2015 177 | 178 | ###### Backwards compatible bug fixes 179 | - #18 - Cannot read property 'http' of undefined 180 | - #27 - logResponse doesn't reject when this.http() rejects with something that is not an Error 181 | 182 | ##### 2.1.0 - 11 September 2015 183 | 184 | ###### Backwards compatible API changes 185 | - #20 - DSHttpAdapter.POST does not pick DSHttpAdapter.defaults.basePath 186 | - #25 - Allow urlPath override for httpAdapter PR by @internalfx 187 | - #26 - Add support for full url override 188 | 189 | ###### Backwards compatible bug fixes 190 | - #21 - Cannot read property 'method' of undefined 191 | - #22 - Fixing issue where logging responses cannot handle Error objects. PR by @RobertHerhold 192 | 193 | ##### 2.0.0 - 02 July 2015 194 | 195 | Stable Version 2.0.0 196 | 197 | ##### 2.0.0-rc.1 - 30 June 2015 198 | 199 | Added `getEndpoint()`, which was removed from JSData 200 | 201 | ##### 2.0.0-beta.1 - 17 April 2015 202 | 203 | Prepare for 2.0 204 | 205 | ##### 1.2.3 - 07 March 2015 206 | 207 | ###### Other 208 | - Converted code to ES6. 209 | 210 | ##### 1.2.2 - 04 March 2015 211 | 212 | ###### Backwards compatible bug fixes 213 | - #10 - DSHttpAdapter#find does not call queryTransform 214 | 215 | ###### Other 216 | - Switched build to webpack. UMD should actually work now. 217 | 218 | ##### 1.2.1 - 25 February 2015 219 | 220 | ###### Backwards compatible bug fixes 221 | - #9 - Does not properly throw error in find() (like other adapters) when the item cannot be found 222 | 223 | ##### 1.2.0 - 24 February 2015 224 | 225 | ###### Backwards compatible API changes 226 | - Added `suffix` option 227 | 228 | ##### 1.1.0 - 04 February 2015 229 | 230 | Now requiring js-data 1.1.0 to allow for safe stringification of cyclic objects 231 | 232 | ##### 1.0.0 - 03 February 2015 233 | 234 | Stable Version 1.0.0 235 | 236 | ##### 1.0.0-beta.2 - 10 January 2015 237 | 238 | Fixed some tests. 239 | 240 | ##### 1.0.0-beta.1 - 10 January 2015 241 | 242 | Now in beta 243 | 244 | ##### 1.0.0-alpha.6 - 05 December 2014 245 | 246 | ###### Backwards compatible bug fixes 247 | - Fix for making copies of `options` 248 | 249 | ##### 1.0.0-alpha.5 - 05 December 2014 250 | 251 | ###### Backwards compatible bug fixes 252 | - Updated dependencies 253 | - Now safely making copies of the `options` passed into methods 254 | 255 | ##### 1.0.0-alpha.4 - 01 December 2014 256 | 257 | ###### Backwards compatible API changes 258 | - Added DSHttpAdapter.getPath 259 | 260 | ###### Backwards compatible bug fixes 261 | - #8 - Fixed handling of `forceTrailingSlash` option 262 | 263 | ##### 1.0.0-alpha.3 - 19 November 2014 264 | 265 | ###### Breaking API changes 266 | - `queryTransform`, `serialize` and `deserialize` now take the resource definition as the first argument instead of just the resource name 267 | 268 | ##### 1.0.0-alpha.2 - 01 November 2014 269 | 270 | ###### Backwards compatible API changes 271 | - #4 - Log failures. See also #5 and #6 272 | 273 | ##### 1.0.0-alpha.1 - 31 October 2014 274 | 275 | ###### Backwards compatible bug fixes 276 | - #3 - _this.defaults.log() throws Illegal invocation 277 | 278 | ##### 0.4.2 - 22 October 2014 279 | 280 | ###### Backwards compatible bug fixes 281 | - Fixed illegal invocation error 282 | 283 | ##### 0.4.1 - 30 September 2014 284 | 285 | ###### Backwards compatible API changes 286 | - Added `forceTrailingSlash` option to `DSHttpAdapter#defaults` 287 | 288 | ###### Other 289 | - Improved checking for the js-data dependency 290 | 291 | ##### 0.4.0 - 25 September 2014 292 | 293 | ###### Breaking API changes 294 | - Refactored from `baseUrl` to `basePath`, as `baseUrl` doesn't make sense for all adapters, but `basePath` does 295 | 296 | ##### 0.3.0 - 21 September 2014 297 | 298 | ###### Backwards compatible API changes 299 | - Small re-organization. 300 | 301 | ##### 0.2.0 - 21 September 2014 302 | 303 | ###### Backwards compatible API changes 304 | - Added deserialize and serialize. 305 | 306 | ##### 0.1.0 - 17 September 2014 307 | 308 | - Initial release 309 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of js-data-http project contributors. 2 | # 3 | # Names are formatted as: 4 | # Name 5 | # 6 | InternalFX 7 | Ivan Voznyakovsky 8 | Jason Dobry 9 | Josh Drake 10 | RobertHerhold 11 | Tomás Fox 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 js-data-http project authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js-data logo 2 | 3 | # js-data-http 4 | 5 | [![Slack Status][sl_b]][sl_l] 6 | [![npm version][npm_b]][npm_l] 7 | [![npm downloads][dn_b]][dn_l] 8 | [![Circle CI][circle_b]][circle_l] 9 | [![Coverage Status][cov_b]][cov_l] 10 | 11 | This repo contains HTTP adapters for [js-data](http://www.js-data.io/): 12 | 13 | - js-data-http - HTTP (XHR, includes [`axios`][axios]) adapter for JSData in the 14 | browser. Capable of using `window.fetch` instead of axios. __Only works in the browser__. 15 | - js-data-fetch - Same as `js-data-http` but _does not_ include `axios` and will use 16 | `window.fetch` if available and if you don't provide your own http library. 17 | - js-data-http-node - Same as `js-data-http` but runs on Node.js. Depends on `axios` 18 | and will use `axios` unless you provide a different http library. 19 | 20 | Tested on IE9, Chrome 46, Firefox 41 & Safari 7.1 using 21 | bs logo 22 | 23 | To get started, visit the main website at __[http://js-data.io](http://www.js-data.io)__. 24 | 25 | ## Links 26 | 27 | * [Quick start](http://www.js-data.io/v3.0/docs/home#quick-start) - Get started in 5 minutes 28 | * [Guides and Tutorials](http://www.js-data.io/v3.0/docs/home) - Learn how to use JSData 29 | * [HttpAdapter Guide](http://www.js-data.io/v3.0/docs/js-data-http) - Learn how to use the HttpAdapter 30 | * [API Reference Docs](http://api.js-data.io) - Explore components, methods, options, etc. 31 | * [Community & Support](http://js-data.io/docs/community) - Find solutions and chat with the community 32 | * [General Contributing Guide](http://js-data.io/docs/contributing) - Give back and move the project forward 33 | * [Contributing to js-data-http](https://github.com/js-data/js-data-http/blob/master/.github/CONTRIBUTING.md) 34 | 35 | ## License 36 | 37 | The MIT License (MIT) 38 | 39 | Copyright (c) 2014-2016 js-data-http project authors 40 | 41 | * [LICENSE](https://github.com/js-data/js-data-http/blob/master/LICENSE) 42 | * [AUTHORS](https://github.com/js-data/js-data-http/blob/master/AUTHORS) 43 | * [CONTRIBUTORS](https://github.com/js-data/js-data-http/blob/master/CONTRIBUTORS) 44 | 45 | [sl_b]: http://slack.js-data.io/badge.svg 46 | [sl_l]: http://slack.js-data.io 47 | [npm_b]: https://img.shields.io/npm/v/js-data-http.svg?style=flat 48 | [npm_l]: https://www.npmjs.org/package/js-data-http 49 | [dn_b]: https://img.shields.io/npm/dm/js-data-http.svg?style=flat 50 | [dn_l]: https://www.npmjs.org/package/js-data-http 51 | [circle_b]: https://img.shields.io/circleci/project/js-data/js-data-http/master.svg?style=flat 52 | [circle_l]: https://circleci.com/gh/js-data/js-data-http/tree/master 53 | [cov_b]: https://img.shields.io/codecov/c/github/js-data/js-data-http/3.0.svg?style=flat 54 | [cov_l]: https://codecov.io/github/js-data/js-data-http 55 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-http", 3 | "description": "HTTP (XHR) adapter for js-data in the browser.", 4 | "homepage": "https://github.com/js-data/js-data-http", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/js-data/js-data-http.git" 8 | }, 9 | "author": "js-data-http project authors", 10 | "main": "./dist/js-data-http.js", 11 | "ignore": [ 12 | ".idea/", 13 | ".*", 14 | "*.iml", 15 | "src/", 16 | "doc/", 17 | "build_examples/", 18 | "coverage/", 19 | "scripts/", 20 | "junit/", 21 | "node_modules/", 22 | "fetch/", 23 | "node/", 24 | "test/", 25 | "package.json", 26 | "karma.conf.js", 27 | "karma.start.js", 28 | "webpack.config.js" 29 | ], 30 | "dependencies": { 31 | "js-data": ">=3.0.0-beta.6" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /build_examples/browserify/README.md: -------------------------------------------------------------------------------- 1 | Running `browserify -x axios app.js > bundle.js` in this directory will produce `bundle.js` 2 | 3 | Note the external dependency "axios" that is excluded from the build (it's not needed when using js-data-angular). 4 | -------------------------------------------------------------------------------- /build_examples/browserify/app.js: -------------------------------------------------------------------------------- 1 | var JSData = require('js-data') 2 | // normally this would be "var HttpAdapter = require('js-data-http')" 3 | var HttpAdapter = require('../../') 4 | 5 | document.getElementById('main').innerHTML = HttpAdapter.version.full 6 | 7 | var adapter = new HttpAdapter() 8 | var store = new JSData.DataStore() 9 | store.registerAdapter('http', adapter, { default: true }) 10 | store.defineMapper('user') 11 | 12 | store.find('user', 1).catch(function (err) { 13 | console.log(err) 14 | }) 15 | -------------------------------------------------------------------------------- /build_examples/browserify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 |

8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/node/app.js: -------------------------------------------------------------------------------- 1 | var JSData = require('js-data') 2 | // normally this would be "var HttpAdapter = require('js-data-http-node')" 3 | var HttpAdapter = require('../../') 4 | 5 | var adapter = new HttpAdapter() 6 | var store = new JSData.Container() 7 | store.registerAdapter('http', adapter, { default: true }) 8 | store.defineMapper('user') 9 | 10 | store.find('user', 1).catch(function (err) { 11 | console.log(err) 12 | }) 13 | -------------------------------------------------------------------------------- /build_examples/r.js/README.md: -------------------------------------------------------------------------------- 1 | Running `r.js -o require.config.js` in this directory will produce `bundle.js` 2 | 3 | In `index.html` switch `script/main` between `main` (load scripts dynamically) and `bundle` (load bundled scripts) 4 | -------------------------------------------------------------------------------- /build_examples/r.js/app.js: -------------------------------------------------------------------------------- 1 | define('app', [ 2 | 'js-data', 3 | 'js-data-http' 4 | ], function (JSData, HttpAdapter) { 5 | document.getElementById('main').innerHTML = HttpAdapter.version.full 6 | 7 | var adapter = new HttpAdapter() 8 | var store = new DataStore() 9 | store.registerAdapter('http', adapter, { default: true }) 10 | store.defineMapper('user') 11 | 12 | store.find('user', 1).catch(function (err) { 13 | console.log(err) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /build_examples/r.js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /build_examples/r.js/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | paths: { 3 | // customize these as needed 4 | 'js-data-http': '../../node/dist/js-data-http', 5 | 'js-data': '../../node_modules/js-data/dist/js-data', 6 | } 7 | }); 8 | 9 | require([ 10 | 'app' 11 | ], function (User) { 12 | 13 | User.find(1).catch(function (err) { 14 | console.log(err); 15 | }); 16 | } 17 | ); 18 | -------------------------------------------------------------------------------- /build_examples/r.js/require.config.js: -------------------------------------------------------------------------------- 1 | ({ 2 | name: 'main', 3 | mainConfigFile: 'main.js', 4 | out: 'bundle.js', 5 | optimize: 'none' 6 | }) 7 | -------------------------------------------------------------------------------- /build_examples/webpack/README.md: -------------------------------------------------------------------------------- 1 | Running `webpack` in this directory will produce `bundle.js` 2 | 3 | Note the external dependency "axios" that is excluded from the build (it's not needed when using js-data-angular). 4 | -------------------------------------------------------------------------------- /build_examples/webpack/app.js: -------------------------------------------------------------------------------- 1 | var JSData = require('js-data') 2 | // normally this would be "var HttpAdapter = require('js-data-http')" 3 | var HttpAdapter = require('../../') 4 | 5 | document.getElementById('main').innerHTML = HttpAdapter.version.full 6 | 7 | var adapter = new HttpAdapter() 8 | var store = new JSData.DataStore() 9 | store.registerAdapter('http', adapter, { default: true }) 10 | store.defineMapper('user') 11 | 12 | store.find('user', 1).catch(function (err) { 13 | console.log(err) 14 | }) 15 | -------------------------------------------------------------------------------- /build_examples/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 |

8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './app.js', 3 | output: { 4 | filename: 'bundle.js' 5 | }, 6 | // only necessary for this demo 7 | resolve: { 8 | alias: { 9 | 'js-data-http': '../../dist/js-data-http.js' 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /build_examples/webpack_es6/README.md: -------------------------------------------------------------------------------- 1 | Running `webpack` in this directory will produce `bundle.js` 2 | 3 | Note the external dependency "axios" that is excluded from the build (it's not needed when using js-data-angular). 4 | -------------------------------------------------------------------------------- /build_examples/webpack_es6/app.js: -------------------------------------------------------------------------------- 1 | import {DataStore} from 'js-data' 2 | // normally this would be "import DSHttpAdatper from 'js-data-http'" 3 | import HttpAdapter from '../../'; 4 | 5 | document.getElementById('main').innerHTML = HttpAdapter.version.full; 6 | 7 | var adapter = new HttpAdapter() 8 | var store = new DataStore() 9 | store.registerAdapter('http', adapter, { default: true }) 10 | store.defineMapper('user') 11 | 12 | store.find('user', 1).catch(function (err) { 13 | console.log(err) 14 | }) -------------------------------------------------------------------------------- /build_examples/webpack_es6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 |

8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/webpack_es6/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = { 3 | entry: './app.js', 4 | output: { 5 | filename: 'bundle.js' 6 | }, 7 | // only necessary for this demo 8 | resolve: { 9 | alias: { 10 | 'js-data-http': '../../dist/js-data-http.js' 11 | } 12 | }, 13 | module: { 14 | loaders: [ 15 | {test: /(.+)\.js$/, loader: 'babel-loader?blacklist=useStrict'} 16 | ] 17 | }, 18 | resolveLoader: { 19 | root: path.join(__dirname, '../../node_modules') 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | docker: 5 | - image: circleci/node:10 6 | steps: 7 | - checkout 8 | - run: npm install 9 | - run: sudo npm i -g codecov 10 | - run: 11 | name: Test 12 | command: npm test 13 | - run: 14 | name: Generate code coverage 15 | command: cat ./coverage/lcov.info | codecov 16 | - store_artifacts: 17 | path: dist 18 | destination: fetch/dist 19 | 20 | workflows: 21 | version: 2 22 | build_and_test: 23 | jobs: 24 | - test: 25 | filters: 26 | branches: 27 | ignore: gh-pages 28 | -------------------------------------------------------------------------------- /conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "includePattern": ".*js$" 4 | }, 5 | "plugins": ["plugins/markdown"], 6 | "opts": { 7 | "template": "./node_modules/ink-docstrap/template", 8 | "destination": "./doc/", 9 | "recurse": true, 10 | "verbose": true, 11 | "readme": "./README.md", 12 | "package": "./package.json" 13 | }, 14 | "templates": { 15 | "theme": "jsdata", 16 | "systemName": "js-data-http", 17 | "copyright": "js-data-http Copyright © 2014-2016 js-data-http project authors", 18 | "outputSourceFiles": true, 19 | "linenums": true, 20 | "logoFile": "https://raw.githubusercontent.com/js-data/js-data/master/js-data-64.png", 21 | "footer": "", 22 | "analytics": { 23 | "ua": "UA-55528236-2", 24 | "domain": "api.js-data.io" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /dist/js-data-http.d.ts: -------------------------------------------------------------------------------- 1 | import {Adapter} from 'js-data-adapter' 2 | 3 | interface IDict { 4 | [key: string]: any; 5 | } 6 | interface IActionOpts { 7 | adapter?: string, 8 | pathname?: string, 9 | request?: Function, 10 | response?: Function, 11 | responseError?: Function 12 | } 13 | interface IBaseAdapter extends IDict { 14 | debug?: boolean, 15 | raw?: boolean 16 | } 17 | interface IBaseHttpAdapter extends IBaseAdapter { 18 | basePath?: string 19 | forceTrailingSlash?: boolean 20 | http?: any 21 | httpConfig?: IDict 22 | suffix?: string 23 | useFetch?: boolean 24 | } 25 | export function addAction(name: string, opts: IActionOpts): Function 26 | export function addActions(opts ?: { [key:string]: IActionOpts }): Function 27 | export class HttpAdapter extends Adapter { 28 | static version: { 29 | full?: string 30 | minor?: string 31 | major?: string 32 | patch?: string 33 | alpha?: string | boolean 34 | beta?: string | boolean 35 | } 36 | static extend(instanceProps?: IDict, classProps?: IDict): typeof HttpAdapter 37 | constructor(opts?: IBaseHttpAdapter) 38 | afterDEL(url: string, config: any, opts: any, response: any): any 39 | afterGET(url: string, config: any, opts: any, response: any): any 40 | afterHTTP(config: any, opts: any, response: any): any 41 | afterPOST(url: string, data: any, config: any, opts: any, response: any): any 42 | afterPUT(url: string, data: any, config: any, opts: any, response: any): any 43 | beforeDEL(url: string, config: any, opts: any): any 44 | beforeGET(url: string, config: any, opts: any): any 45 | beforeHTTP(config: any, opts: any): any 46 | beforePOST(url: string, data: any, config: any, opts: any): any 47 | beforePUT(url: string, data: any, config: any, opts: any): any 48 | _count(mapper: any, query: any, opts?: any): Promise 49 | _create(mapper: any, props: any, opts?: any): Promise 50 | _createMany(mapper: any, props: any, opts?: any): Promise 51 | _destroy(mapper: any, id: string | number, opts?: any): Promise 52 | _destroyAll(mapper: any, query: any, opts?: any): Promise 53 | _end(mapper: any, opts: any, response: any): any 54 | _find(mapper: any, id: string | number, opts?: any): Promise 55 | _findAll(mapper: any, query: any, opts?: any): Promise 56 | _sum(mapper: any, field: any, query: any, opts?: any): Promise 57 | _update(mapper: any, id: any, props: any, opts?: any): Promise 58 | _updateAll(mapper: any, props: any, query: any, opts?: any): Promise 59 | _updateMany(mapper: any, records: any, opts?: any): Promise 60 | count(mapper: any, query: any, opts?: any): Promise 61 | create(mapper: any, props: any, opts?: any): Promise 62 | createMany(mapper: any, props: any, opts?: any): Promise 63 | DEL(url: string, config?: any, opts?: any): Promise 64 | deserialize(mapper: any, response: any, opts?: any): any 65 | destroy(mapper: any, id: string | number, opts?: any): Promise 66 | destroyAll(mapper: any, query: any, opts?: any): Promise 67 | error(...args: any[]): void 68 | fetch(config: any, opts?: any): Promise 69 | find(mapper: any, id: string | number, opts?: any): Promise 70 | findAll(mapper: any, query: any, opts?: any): Promise 71 | GET(url: string, config?: any, opts?: any): Promise 72 | getEndpoint(mapper: any, id: string | number, opts?: any): any 73 | getPath(method: string, mapper: any, id: any, opts?: any): any 74 | getParams(opts?: any): any 75 | getSuffix(mapper: any, opts?: any): any 76 | HTTP(config: any, opts?: any): Promise 77 | POST(url: string, data: any, config?: any, opts?: any): Promise 78 | PUT(url: string, data: any, config?: any, opts?: any): Promise 79 | queryTransform(mapper: any, params: any, opts?: any): any 80 | responseError(err: any, config?: any, opts?: any): any 81 | serialize(mapper: any, data: any, opts?: any): Promise 82 | sum(mapper: any, field: any, query: any, opts?: any): Promise 83 | update(mapper: any, id: any, props: any, opts?: any): Promise 84 | updateAll(mapper: any, props: any, query: any, opts?: any): Promise 85 | updateMany(mapper: any, records: any, opts?: any): Promise 86 | } 87 | -------------------------------------------------------------------------------- /fetch/.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.map 3 | node_modules/ 4 | junit/ -------------------------------------------------------------------------------- /fetch/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 js-data-http project authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fetch/dist/js-data-fetch.d.ts: -------------------------------------------------------------------------------- 1 | import {Adapter} from 'js-data-adapter' 2 | 3 | interface IDict { 4 | [key: string]: any; 5 | } 6 | interface IActionOpts { 7 | adapter?: string, 8 | pathname?: string, 9 | request?: Function, 10 | response?: Function, 11 | responseError?: Function 12 | } 13 | interface IBaseAdapter extends IDict { 14 | debug?: boolean, 15 | raw?: boolean 16 | } 17 | interface IBaseHttpAdapter extends IBaseAdapter { 18 | basePath?: string 19 | forceTrailingSlash?: boolean 20 | http?: any 21 | httpConfig?: IDict 22 | suffix?: string 23 | useFetch?: boolean 24 | } 25 | export function addAction(name: string, opts: IActionOpts): Function 26 | export function addActions(opts ?: { [key:string]: IActionOpts }): Function 27 | export class HttpAdapter extends Adapter { 28 | static version: { 29 | full?: string 30 | minor?: string 31 | major?: string 32 | patch?: string 33 | alpha?: string | boolean 34 | beta?: string | boolean 35 | } 36 | static extend(instanceProps?: IDict, classProps?: IDict): typeof HttpAdapter 37 | constructor(opts?: IBaseHttpAdapter) 38 | } 39 | -------------------------------------------------------------------------------- /fetch/karma.conf.js: -------------------------------------------------------------------------------- 1 | var browsers = ['PhantomJS'] 2 | 3 | var customLaunchers = { 4 | sl_chrome: { 5 | base: 'SauceLabs', 6 | browserName: 'chrome', 7 | platform: 'Windows 10', 8 | version: 'latest' 9 | }, 10 | sl_firefox: { 11 | base: 'SauceLabs', 12 | browserName: 'firefox', 13 | platform: 'Windows 10', 14 | version: 'latest' 15 | }, 16 | sl_safari_9: { 17 | base: 'SauceLabs', 18 | browserName: 'safari', 19 | platform: 'OS X 10.11', 20 | version: '9.0' 21 | }, 22 | sl_edge: { 23 | base: 'SauceLabs', 24 | browserName: 'microsoftedge', 25 | platform: 'Windows 10', 26 | version: 'latest' 27 | }, 28 | sl_ie_11: { 29 | base: 'SauceLabs', 30 | browserName: 'internet explorer', 31 | platform: 'Windows 8.1', 32 | version: '11' 33 | }, 34 | sl_ie_10: { 35 | base: 'SauceLabs', 36 | browserName: 'internet explorer', 37 | platform: 'Windows 2012', 38 | version: '10' 39 | }, 40 | sl_ie_9: { 41 | base: 'SauceLabs', 42 | browserName: 'internet explorer', 43 | platform: 'Windows 2008', 44 | version: '9' 45 | }, 46 | sl_android_5: { 47 | base: 'SauceLabs', 48 | browserName: 'android', 49 | platform: 'Linux', 50 | version: '5.1' 51 | } 52 | } 53 | 54 | if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) { 55 | browsers = browsers.concat(Object.keys(customLaunchers)) 56 | } 57 | 58 | module.exports = function (config) { 59 | config.set({ 60 | basePath: '../', 61 | frameworks: ['sinon', 'chai', 'mocha'], 62 | plugins: [ 63 | 'karma-sinon', 64 | 'karma-mocha', 65 | 'karma-chai', 66 | 'karma-phantomjs-launcher', 67 | 'karma-sauce-launcher' 68 | ], 69 | autoWatch: false, 70 | browsers: browsers, 71 | files: [ 72 | 'node_modules/babel-polyfill/dist/polyfill.js', 73 | 'node_modules/whatwg-fetch/fetch.js', 74 | 'node_modules/js-data/dist/js-data.js', 75 | 'fetch/dist/js-data-fetch.js', 76 | 'fetch/karma.start.js', 77 | 'test/*.test.js' 78 | ], 79 | sauceLabs: { 80 | testName: 'JSDataHttp fetch Tests', 81 | public: 'public', 82 | recordVideo: false, 83 | recordScreenshots: false, 84 | build: process.env.CIRCLE_BUILD_NUM ? ('circle-' + process.env.CIRCLE_BUILD_NUM) : ('local-' + new Date().getTime()) 85 | }, 86 | customLaunchers: customLaunchers, 87 | reporters: ['dots'], 88 | junitReporter: { 89 | outputDir: process.env.CIRCLE_TEST_REPORTS || 'junit', 90 | outputFile: undefined, 91 | suite: 'js-data-http', 92 | userBrowserName: false 93 | }, 94 | port: 9876, 95 | runnerPort: 9100, 96 | colors: true, 97 | logLevel: config.LOG_INFO, 98 | browserNoActivityTimeout: 90000, 99 | captureTimeout: 90000, 100 | singleRun: true 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /fetch/karma.start.js: -------------------------------------------------------------------------------- 1 | /* global JSData:true, JSDataHttp:true, sinon:true, chai:true */ 2 | 3 | before(function () { 4 | var Test = this 5 | Test.TEST_FETCH = window.navigator.userAgent.indexOf('MSIE 9.0') === -1 6 | Test.fail = function (msg) { 7 | if (msg instanceof Error) { 8 | console.log(msg.stack) 9 | } else { 10 | Test.assert.equal('should not reach this!: ' + msg, 'failure') 11 | } 12 | } 13 | Test.assert = chai.assert 14 | Test.assert.objectsEqual = function (a, b, m) { 15 | Test.assert.deepEqual(JSON.parse(JSON.stringify(a)), JSON.parse(JSON.stringify(b)), m || (JSON.stringify(a) + ' should be equal to ' + JSON.stringify(b))) 16 | } 17 | Test.sinon = sinon 18 | Test.JSData = JSData 19 | Test.addAction = JSDataHttp.addAction 20 | Test.addActions = JSDataHttp.addActions 21 | Test.HttpAdapter = JSDataHttp.HttpAdapter 22 | 23 | Test.store = new JSData.DataStore() 24 | Test.adapter = new Test.HttpAdapter() 25 | Test.store.registerAdapter('http', Test.adapter, { default: true }) 26 | 27 | Test.User = new JSData.Mapper({ 28 | name: 'user' 29 | }) 30 | Test.Post = new JSData.Mapper({ 31 | name: 'post', 32 | endpoint: 'posts', 33 | basePath: 'api' 34 | }) 35 | 36 | Test.User.registerAdapter('http', Test.adapter, { default: true }) 37 | Test.Post.registerAdapter('http', Test.adapter, { default: true }) 38 | console.log('Testing against js-data ' + JSData.version.full) 39 | }) 40 | 41 | beforeEach(function () { 42 | var Test = this 43 | 44 | Test.p1 = { author: 'John', age: 30, id: 5 } 45 | Test.p2 = { author: 'Sally', age: 31, id: 6 } 46 | Test.p3 = { author: 'Mike', age: 32, id: 7 } 47 | Test.p4 = { author: 'Adam', age: 33, id: 8 } 48 | Test.p5 = { author: 'Adam', age: 33, id: 9 } 49 | 50 | Test.requests = [] 51 | 52 | Test.adapter.isFetch = true 53 | Test.adapter.http = function (config) { 54 | config.headers || (config.headers = {}) 55 | config.headers.Accept = 'application/json, text/plain, */*' 56 | var params = [] 57 | for (var key in config.params) { 58 | config.params[key] = Test.JSData.utils.isObject(config.params[key]) ? JSON.stringify(config.params[key]) : config.params[key] 59 | params.push([key, config.params[key]]) 60 | } 61 | return new Promise(function (resolve) { 62 | var url = config.url 63 | if (params.length) { 64 | url += '?' 65 | params.forEach(function (param) { 66 | url += param[0] 67 | url += '=' 68 | url += encodeURIComponent(param[1]) 69 | }) 70 | } 71 | var request = { 72 | url: url, 73 | method: config.method, 74 | requestBody: JSON.stringify(config.data), 75 | requestHeaders: config.headers, 76 | respond: function (statusCode, headers, body) { 77 | resolve({ 78 | url: config.url, 79 | method: config.method, 80 | status: statusCode, 81 | headers: headers, 82 | data: body && statusCode >= 200 && statusCode < 300 ? JSON.parse(body) : '' 83 | }) 84 | } 85 | } 86 | Test.requests.push(request) 87 | }) 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-fetch", 3 | "description": "HTTP adapter for js-data that uses the fetch API.", 4 | "version": "3.0.0", 5 | "homepage": "https://github.com/js-data/js-data-http", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-http.git" 9 | }, 10 | "author": "js-data-http project authors", 11 | "license": "MIT", 12 | "main": "./dist/js-data-fetch.js", 13 | "files": [ 14 | "dist/" 15 | ], 16 | "keywords": [ 17 | "ajax", 18 | "axios", 19 | "rest", 20 | "adapter", 21 | "http" 22 | ], 23 | "dependencies": { 24 | "js-data": ">= 3.0.0", 25 | "js-data-adapter": "1.0.0" 26 | }, 27 | "peerDependencies": { 28 | "js-data": ">= 3.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fetch/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import replace from 'rollup-plugin-replace' 3 | 4 | export default { 5 | moduleName: 'JSDataHttp', 6 | amd: { 7 | id: 'js-data-fetch' 8 | }, 9 | external: [ 10 | 'js-data' 11 | ], 12 | globals: { 13 | 'js-data': 'JSData' 14 | }, 15 | plugins: [ 16 | replace({ 17 | 'import axios from \'../node_modules/axios/dist/axios\'': 'var axios = undefined' 18 | }), 19 | babel({ 20 | babelrc: false, 21 | plugins: [ 22 | 'external-helpers' 23 | ], 24 | presets: [ 25 | [ 26 | 'es2015', 27 | { 28 | modules: false 29 | } 30 | ] 31 | ], 32 | exclude: 'node_modules/axios/**' 33 | }) 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /fetch/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-fetch", 3 | "version": false, 4 | "main": "./dist/js-data-fetch.d.ts", 5 | "ambientDependencies": { 6 | "js-data": "npm:js-data", 7 | "js-data-adapter": "npm:js-data-adapter", 8 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var browsers = ['PhantomJS'] 2 | 3 | var customLaunchers = { 4 | sl_chrome: { 5 | base: 'SauceLabs', 6 | browserName: 'chrome', 7 | platform: 'Windows 10', 8 | version: 'latest' 9 | }, 10 | sl_firefox: { 11 | base: 'SauceLabs', 12 | browserName: 'firefox', 13 | platform: 'Windows 10', 14 | version: 'latest' 15 | }, 16 | sl_safari_9: { 17 | base: 'SauceLabs', 18 | browserName: 'safari', 19 | platform: 'OS X 10.11', 20 | version: '9.0' 21 | }, 22 | sl_edge: { 23 | base: 'SauceLabs', 24 | browserName: 'microsoftedge', 25 | platform: 'Windows 10', 26 | version: 'latest' 27 | }, 28 | sl_ie_11: { 29 | base: 'SauceLabs', 30 | browserName: 'internet explorer', 31 | platform: 'Windows 8.1', 32 | version: '11' 33 | }, 34 | sl_ie_10: { 35 | base: 'SauceLabs', 36 | browserName: 'internet explorer', 37 | platform: 'Windows 2012', 38 | version: '10' 39 | }, 40 | sl_ie_9: { 41 | base: 'SauceLabs', 42 | browserName: 'internet explorer', 43 | platform: 'Windows 2008', 44 | version: '9' 45 | }, 46 | sl_android_5: { 47 | base: 'SauceLabs', 48 | browserName: 'android', 49 | platform: 'Linux', 50 | version: '5.1' 51 | } 52 | } 53 | 54 | if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) { 55 | browsers = browsers.concat(Object.keys(customLaunchers)) 56 | } 57 | 58 | module.exports = function (config) { 59 | config.set({ 60 | basePath: './', 61 | frameworks: ['sinon', 'chai', 'mocha'], 62 | plugins: [ 63 | 'karma-sinon', 64 | 'karma-mocha', 65 | 'karma-chai', 66 | 'karma-phantomjs-launcher', 67 | 'karma-sauce-launcher' 68 | ], 69 | autoWatch: false, 70 | browsers: browsers, 71 | files: [ 72 | 'node_modules/babel-polyfill/dist/polyfill.js', 73 | 'node_modules/js-data/dist/js-data.js', 74 | 'dist/js-data-http.js', 75 | 'karma.start.js', 76 | 'test/*.test.js' 77 | ], 78 | sauceLabs: { 79 | testName: 'JSDataHttp Tests', 80 | public: 'public', 81 | recordVideo: false, 82 | recordScreenshots: false, 83 | build: process.env.CIRCLE_BUILD_NUM ? ('circle-' + process.env.CIRCLE_BUILD_NUM) : ('local-' + new Date().getTime()) 84 | }, 85 | customLaunchers: customLaunchers, 86 | reporters: ['dots'], 87 | junitReporter: { 88 | outputDir: process.env.CIRCLE_TEST_REPORTS || 'junit', 89 | outputFile: undefined, 90 | suite: 'js-data-http', 91 | userBrowserName: false 92 | }, 93 | port: 9876, 94 | runnerPort: 9100, 95 | colors: true, 96 | logLevel: config.LOG_INFO, 97 | browserNoActivityTimeout: 90000, 98 | captureTimeout: 90000, 99 | singleRun: true 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /karma.start.js: -------------------------------------------------------------------------------- 1 | /* global JSData:true, JSDataHttp:true, sinon:true, chai:true */ 2 | 3 | before(function () { 4 | var Test = this 5 | Test.TEST_FETCH = false 6 | Test.fail = function (msg) { 7 | if (msg instanceof Error) { 8 | console.log(msg.stack) 9 | } else { 10 | Test.assert.equal('should not reach this!: ' + msg, 'failure') 11 | } 12 | } 13 | Test.assert = chai.assert 14 | Test.assert.objectsEqual = function (a, b, m) { 15 | Test.assert.deepEqual(JSON.parse(JSON.stringify(a)), JSON.parse(JSON.stringify(b)), m || (JSON.stringify(a) + ' should be equal to ' + JSON.stringify(b))) 16 | } 17 | Test.sinon = sinon 18 | Test.JSData = JSData 19 | Test.addAction = JSDataHttp.addAction 20 | Test.addActions = JSDataHttp.addActions 21 | Test.HttpAdapter = JSDataHttp.HttpAdapter 22 | 23 | Test.store = new JSData.DataStore() 24 | Test.adapter = new Test.HttpAdapter() 25 | 26 | Test.store.registerAdapter('http', Test.adapter, { default: true }) 27 | 28 | Test.User = new JSData.Mapper({ 29 | name: 'user' 30 | }) 31 | Test.Post = new JSData.Mapper({ 32 | name: 'post', 33 | endpoint: 'posts', 34 | basePath: 'api' 35 | }) 36 | 37 | Test.User.registerAdapter('http', Test.adapter, { default: true }) 38 | Test.Post.registerAdapter('http', Test.adapter, { default: true }) 39 | console.log('Testing against js-data ' + JSData.version.full) 40 | }) 41 | 42 | beforeEach(function () { 43 | var Test = this 44 | 45 | Test.p1 = { author: 'John', age: 30, id: 5 } 46 | Test.p2 = { author: 'Sally', age: 31, id: 6 } 47 | Test.p3 = { author: 'Mike', age: 32, id: 7 } 48 | Test.p4 = { author: 'Adam', age: 33, id: 8 } 49 | Test.p5 = { author: 'Adam', age: 33, id: 9 } 50 | 51 | try { 52 | Test.xhr = Test.sinon.useFakeXMLHttpRequest() 53 | // Create an array to store requests 54 | Test.requests = [] 55 | // Keep references to created requests 56 | Test.xhr.onCreate = function (xhr) { 57 | Test.requests.push(xhr) 58 | } 59 | } catch (err) { 60 | console.error(err) 61 | } 62 | }) 63 | 64 | afterEach(function () { 65 | var Test = this 66 | // Restore the global timer functions to their native implementations 67 | try { 68 | Test.xhr.restore() 69 | } catch (err) { 70 | console.error(err) 71 | } 72 | }) 73 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.map -------------------------------------------------------------------------------- /node/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 js-data-http project authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /node/dist/js-data-http-node.d.ts: -------------------------------------------------------------------------------- 1 | import {Adapter} from 'js-data-adapter' 2 | 3 | interface IDict { 4 | [key: string]: any; 5 | } 6 | interface IActionOpts { 7 | adapter?: string, 8 | pathname?: string, 9 | request?: Function, 10 | response?: Function, 11 | responseError?: Function 12 | } 13 | interface IBaseAdapter extends IDict { 14 | debug?: boolean, 15 | raw?: boolean 16 | } 17 | interface IBaseHttpAdapter extends IBaseAdapter { 18 | basePath?: string 19 | forceTrailingSlash?: boolean 20 | http?: any 21 | httpConfig?: IDict 22 | suffix?: string 23 | useFetch?: boolean 24 | } 25 | export function addAction(name: string, opts: IActionOpts): Function 26 | export function addActions(opts?: { [key: string]: IActionOpts }): Function 27 | export class HttpAdapter extends Adapter { 28 | static version: { 29 | full?: string 30 | minor?: string 31 | major?: string 32 | patch?: string 33 | alpha?: string | boolean 34 | beta?: string | boolean 35 | } 36 | static extend(instanceProps?: IDict, classProps?: IDict): typeof HttpAdapter 37 | constructor(opts?: IBaseHttpAdapter) 38 | } 39 | -------------------------------------------------------------------------------- /node/mocha.start.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var querystring = require('querystring') 4 | require('source-map-support').install() 5 | 6 | before(function () { 7 | var Test = this 8 | Test.fail = function (msg) { 9 | if (msg instanceof Error) { 10 | console.log(msg.stack) 11 | } else { 12 | Test.assert.equal('should not reach this!: ' + msg, 'failure') 13 | } 14 | } 15 | Test.assert = require('chai').assert 16 | Test.assert.objectsEqual = function (a, b, m) { 17 | Test.assert.deepEqual(JSON.parse(JSON.stringify(a)), JSON.parse(JSON.stringify(b)), m || (JSON.stringify(a) + ' should be equal to ' + JSON.stringify(b))) 18 | } 19 | Test.sinon = require('sinon') 20 | Test.JSData = require('js-data') 21 | Test.addAction = require('./dist/js-data-http-node').addAction 22 | Test.addActions = require('./dist/js-data-http-node').addActions 23 | Test.HttpAdapter = require('./dist/js-data-http-node').HttpAdapter 24 | Test.store = new Test.JSData.DataStore() 25 | Test.adapter = new Test.HttpAdapter() 26 | Test.store.registerAdapter('http', Test.adapter, { default: true }) 27 | 28 | Test.User = new Test.JSData.Mapper({ 29 | name: 'user' 30 | }) 31 | Test.Post = new Test.JSData.Mapper({ 32 | name: 'post', 33 | endpoint: 'posts', 34 | basePath: 'api' 35 | }) 36 | 37 | Test.User.registerAdapter('http', Test.adapter, { default: true }) 38 | Test.Post.registerAdapter('http', Test.adapter, { default: true }) 39 | console.log('Testing against js-data ' + Test.JSData.version.full) 40 | }) 41 | 42 | beforeEach(function () { 43 | var Test = this 44 | Test.p1 = { author: 'John', age: 30, id: 5 } 45 | Test.p2 = { author: 'Sally', age: 31, id: 6 } 46 | Test.p3 = { author: 'Mike', age: 32, id: 7 } 47 | Test.p4 = { author: 'Adam', age: 33, id: 8 } 48 | Test.p5 = { author: 'Adam', age: 33, id: 9 } 49 | 50 | Test.requests = [] 51 | 52 | Test.adapter.isNode = true 53 | Test.adapter.http = function (config) { 54 | config.headers || (config.headers = {}) 55 | config.headers.Accept = 'application/json, text/plain, */*' 56 | var params = 0 57 | for (var key in config.params) { 58 | config.params[key] = Test.JSData.utils.isObject(config.params[key]) ? JSON.stringify(config.params[key]) : config.params[key] 59 | params++ 60 | } 61 | return new Promise(function (resolve) { 62 | var url = config.url 63 | if (params) { 64 | url += '?' 65 | url += querystring.stringify(config.params) 66 | } 67 | var request = { 68 | url: url, 69 | method: config.method, 70 | requestBody: JSON.stringify(config.data), 71 | requestHeaders: config.headers, 72 | respond: function (statusCode, headers, body) { 73 | resolve({ 74 | url: config.url, 75 | method: config.method, 76 | status: statusCode, 77 | headers: headers, 78 | data: body && statusCode >= 200 && statusCode < 300 ? JSON.parse(body) : '' 79 | }) 80 | } 81 | } 82 | Test.requests.push(request) 83 | }) 84 | } 85 | }) 86 | -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-http-node", 3 | "description": "Node.js HTTP adapter for js-data.", 4 | "version": "3.0.0", 5 | "homepage": "https://github.com/js-data/js-data-http", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-http.git" 9 | }, 10 | "author": "js-data-http project authors", 11 | "license": "MIT", 12 | "main": "./dist/js-data-http-node.js", 13 | "files": [ 14 | "dist/" 15 | ], 16 | "keywords": [ 17 | "ajax", 18 | "axios", 19 | "rest", 20 | "adapter", 21 | "http", 22 | "node.js" 23 | ], 24 | "dependencies": { 25 | "js-data": ">= 3.0.0", 26 | "js-data-adapter": "1.0.0" 27 | }, 28 | "peerDependencies": { 29 | "axios": "0.16.2", 30 | "js-data": ">= 3.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /node/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import replace from 'rollup-plugin-replace' 3 | 4 | export default { 5 | moduleName: 'JSDataHttpNode', 6 | amd: { 7 | id: 'js-data-http-node' 8 | }, 9 | external: [ 10 | 'axios', 11 | 'js-data', 12 | 'js-data-adapter' 13 | ], 14 | globals: { 15 | 'axios': 'axios', 16 | 'js-data': 'JSData', 17 | 'js-data-adapter': 'JSDataAdapter' 18 | }, 19 | plugins: [ 20 | replace({ 21 | 'import axios from \'../node_modules/axios/dist/axios\'': 'var axios = require(\'axios\')', 22 | '\'../node_modules/js-data-adapter/src/index\'': '\'js-data-adapter\'' 23 | }), 24 | babel({ 25 | babelrc: false, 26 | plugins: [ 27 | 'external-helpers' 28 | ], 29 | presets: [ 30 | [ 31 | 'es2015', 32 | { 33 | modules: false 34 | } 35 | ] 36 | ], 37 | exclude: 'node_modules/axios/**' 38 | }) 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /node/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-http-node", 3 | "version": false, 4 | "main": "./dist/js-data-http-node.d.ts", 5 | "ambientDependencies": { 6 | "js-data": "npm:js-data", 7 | "js-data-adapter": "npm:js-data-adapter", 8 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-http", 3 | "description": "HTTP (XHR) adapter for js-data in the browser.", 4 | "version": "3.0.1", 5 | "homepage": "https://github.com/js-data/js-data-http", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-http.git" 9 | }, 10 | "author": "js-data-http project authors", 11 | "license": "MIT", 12 | "main": "./dist/js-data-http.js", 13 | "typings": "./dist/js-data-http.d.ts", 14 | "files": [ 15 | "dist/", 16 | "src/", 17 | "AUTHORS", 18 | "CONTRIBUTORS", 19 | "typings.json" 20 | ], 21 | "keywords": [ 22 | "ajax", 23 | "axios", 24 | "rest", 25 | "adapter", 26 | "http", 27 | "fetch", 28 | "browser", 29 | "xhr" 30 | ], 31 | "standard": { 32 | "parser": "babel-eslint", 33 | "globals": [ 34 | "Headers", 35 | "fetch", 36 | "Request", 37 | "action", 38 | "describe", 39 | "it", 40 | "sinon", 41 | "before", 42 | "after", 43 | "beforeEach", 44 | "afterEach" 45 | ], 46 | "ignore": [ 47 | "build_examples/", 48 | "dist/" 49 | ] 50 | }, 51 | "scripts": { 52 | "lint": "standard lint '**/*.js'", 53 | "doc": "jsdoc -c conf.json src node_modules/js-data-adapter/src node_modules/js-data/src/Component.js", 54 | "version": "repo-tools write-version dist/js-data-http.js fetch/dist/js-data-fetch.js node/dist/js-data-http-node.js", 55 | "bundle_http": "rollup src/index.js -c -o dist/js-data-http.js -m dist/js-data-http.js.map -f umd", 56 | "bundle_fetch": "rollup src/index.js -c fetch/rollup.config.js -o fetch/dist/js-data-fetch.js -m fetch/dist/js-data-fetch.js.map -f umd", 57 | "bundle_node": "rollup src/index.js -c node/rollup.config.js -o node/dist/js-data-http-node.js -m node/dist/js-data-http-node.js.map -f cjs", 58 | "bundle": "npm run bundle_http && npm run bundle_fetch && npm run bundle_node && npm run version", 59 | "min": "uglifyjs -o dist/js-data-http.min.js --source-map url=js-data-http.min.map -v -m -c --keep-fnames --screw-ie8 -- dist/js-data-http.js", 60 | "min_fetch": "uglifyjs -o fetch/dist/js-data-fetch.min.js --source-map url=js-data-fetch.min.map -v -m -c --keep-fnames --screw-ie8 -- fetch/dist/js-data-fetch.js", 61 | "banner": "node scripts/banner.js", 62 | "gzip": "echo js-data-http gzipped size: $(cat dist/js-data-http.min.js | gzip -f9 | wc -c)kb", 63 | "gzip_fetch": "echo js-data-fetch gzipped size: $(cat fetch/dist/js-data-fetch.min.js | gzip -f9 | wc -c)kb", 64 | "build": "npm run lint && npm run bundle && npm run min && npm run min_fetch && npm run banner", 65 | "karma": "karma start", 66 | "karma_fetch": "karma start fetch/karma.conf.js", 67 | "mocha": "mocha -t 20000 -R spec node/mocha.start.js test/*.test.js", 68 | "cover": "istanbul cover -x node/dist/js-data-http-node-tests.js --hook-run-in-context node_modules/mocha/bin/_mocha -- -t 20000 -R dot node/mocha.start.js test/*.test.js", 69 | "test": "npm run build && npm run karma && npm run karma_fetch && npm run cover", 70 | "release": "npm test && npm run doc && repo-tools updates && repo-tools changelog && repo-tools authors" 71 | }, 72 | "peerDependencies": { 73 | "js-data": ">= 3.0.0" 74 | }, 75 | "dependencies": { 76 | "js-data-adapter": "1.x.x" 77 | }, 78 | "devDependencies": { 79 | "axios": "0.16.2", 80 | "babel-eslint": "^7.2.3", 81 | "babel-plugin-external-helpers": "6.22.0", 82 | "babel-plugin-syntax-async-functions": "6.13.0", 83 | "babel-plugin-transform-regenerator": "^6.24.1", 84 | "babel-polyfill": "^6.23.0", 85 | "babel-preset-es2015": "6.24.1", 86 | "babel-preset-stage-0": "6.24.1", 87 | "chai": "^4.0.2", 88 | "ink-docstrap": "git+https://github.com/js-data/docstrap.git#cfbe45fa313e1628c493076d5e15d2b855dfbf2c", 89 | "istanbul": "0.4.5", 90 | "js-data-repo-tools": "1.0.0", 91 | "jsdoc": "^3.4.3", 92 | "karma": "^1.7.0", 93 | "karma-browserstack-launcher": "1.3.0", 94 | "karma-chai": "0.1.0", 95 | "karma-mocha": "1.3.0", 96 | "karma-phantomjs-launcher": "1.0.4", 97 | "karma-sauce-launcher": "^1.2.0", 98 | "karma-sinon": "1.0.5", 99 | "mocha": "^3.4.2", 100 | "phantomjs-prebuilt": "^2.1.14", 101 | "rollup": "^0.43.0", 102 | "rollup-plugin-babel": "2.7.1", 103 | "rollup-plugin-commonjs": "8.0.2", 104 | "rollup-plugin-replace": "1.1.1", 105 | "sinon": "^2.3.6", 106 | "standard": "^10.0.2", 107 | "uglify-js": "^3.0.23", 108 | "whatwg-fetch": "^2.0.3" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | 4 | export default { 5 | moduleName: 'JSDataHttp', 6 | amd: { 7 | id: 'js-data-http' 8 | }, 9 | external: [ 10 | 'js-data' 11 | ], 12 | globals: { 13 | 'js-data': 'JSData' 14 | }, 15 | plugins: [ 16 | commonjs({ 17 | include: 'node_modules/axios/**' 18 | }), 19 | babel({ 20 | babelrc: false, 21 | plugins: [ 22 | 'external-helpers' 23 | ], 24 | presets: [ 25 | [ 26 | 'es2015', 27 | { 28 | modules: false 29 | } 30 | ] 31 | ], 32 | exclude: 'node_modules/axios/**' 33 | }) 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /scripts/banner.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var pkg = require('../package.json') 3 | 4 | var banner = '/*!\n' + 5 | '* js-data-http\n' + 6 | '* @version ' + pkg.version + ' - Homepage \n' + 7 | '* @copyright (c) 2014-2016 js-data-http project authors\n' + 8 | '* @license MIT \n' + 9 | '*\n' + 10 | '* @overview HTTP (XHR) adapter for js-data in the browser.\n' + 11 | '*/\n' 12 | 13 | console.log('Adding banner to dist/ files...') 14 | 15 | function addBanner (filepath) { 16 | var contents = fs.readFileSync(filepath, { 17 | encoding: 'utf-8' 18 | }) 19 | if (contents.substr(0, 3) !== '/*!') { 20 | fs.writeFileSync(filepath, banner + contents, { 21 | encoding: 'utf-8' 22 | }) 23 | } 24 | } 25 | 26 | addBanner('dist/js-data-http.js') 27 | addBanner('dist/js-data-http.min.js') 28 | 29 | banner = '/*!\n' + 30 | '* js-data-fetch\n' + 31 | '* @version ' + pkg.version + ' - Homepage \n' + 32 | '* @copyright (c) 2014-2016 js-data-http project authors\n' + 33 | '* @license MIT \n' + 34 | '*\n' + 35 | '* @overview HTTP adapter for js-data that uses the fetch API.\n' + 36 | '*/\n' 37 | 38 | addBanner('fetch/dist/js-data-fetch.js') 39 | addBanner('fetch/dist/js-data-fetch.min.js') 40 | 41 | console.log('Done!') 42 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { utils } from 'js-data' 2 | import axios from '../node_modules/axios/dist/axios' 3 | import { 4 | Adapter, 5 | noop, 6 | noop2 7 | } from '../node_modules/js-data-adapter/src/index' 8 | 9 | let hasFetch = false 10 | 11 | try { 12 | hasFetch = window && window.fetch 13 | } catch (e) {} 14 | 15 | function isValidString (value) { 16 | return (value != null && value !== '') 17 | } 18 | function join (items, separator) { 19 | separator || (separator = '') 20 | return items.filter(isValidString).join(separator) 21 | } 22 | function makePath (...args) { 23 | let result = join(args, '/') 24 | return result.replace(/([^:/]|^)\/{2,}/g, '$1/') 25 | } 26 | 27 | function encode (val) { 28 | return encodeURIComponent(val) 29 | .replace(/%40/gi, '@') 30 | .replace(/%3A/gi, ':') 31 | .replace(/%24/g, '$') 32 | .replace(/%2C/gi, ',') 33 | .replace(/%20/g, '+') 34 | .replace(/%5B/gi, '[') 35 | .replace(/%5D/gi, ']') 36 | } 37 | 38 | function buildUrl (url, params) { 39 | if (!params) { 40 | return url 41 | } 42 | 43 | const parts = [] 44 | 45 | utils.forOwn(params, function (val, key) { 46 | if (val === null || typeof val === 'undefined') { 47 | return 48 | } 49 | if (!utils.isArray(val)) { 50 | val = [val] 51 | } 52 | 53 | val.forEach(function (v) { 54 | if (toString.call(v) === '[object Date]') { 55 | v = v.toISOString() 56 | } else if (utils.isObject(v)) { 57 | v = utils.toJson(v) 58 | } 59 | parts.push(`${encode(key)}=${encode(v)}`) 60 | }) 61 | }) 62 | 63 | if (parts.length > 0) { 64 | url += (url.indexOf('?') === -1 ? '?' : '&') + parts.join('&') 65 | } 66 | 67 | return url 68 | } 69 | 70 | const DEFAULTS = { 71 | /** 72 | * Set a base path in order to use absolute URLs instead of relative URLs. 73 | * 74 | * @example 75 | * const httpAdapter = new HttpAdapter({ 76 | * basePath: 'https://mydomain.com' 77 | * }); 78 | * 79 | * @name HttpAdapter#basePath 80 | * @type {string} 81 | * @since 3.0.0 82 | */ 83 | basePath: '', 84 | 85 | /** 86 | * Ensure that the request url has a trailing forward slash. 87 | * 88 | * @name HttpAdapter#forceTrailingSlash 89 | * @type {boolean} 90 | * @default false 91 | * @since 3.0.0 92 | */ 93 | forceTrailingSlash: false, 94 | 95 | hasFetch: hasFetch, 96 | 97 | /** 98 | * The HTTP function that actually makes the HTTP request. By default this is 99 | * `axios`. 100 | * 101 | * @name HttpAdapter#http 102 | * @type {function} 103 | * @since 3.0.0 104 | * @see http://www.js-data.io/docs/js-data-http#using-a-custom-http-library 105 | */ 106 | http: axios, 107 | 108 | /** 109 | * Default configuration options to be mixed into the `config` argument passed 110 | * to {@link HttpAdapter#http}. 111 | * 112 | * @name HttpAdapter#httpConfig 113 | * @type {object} 114 | * @since 3.0.0 115 | */ 116 | httpConfig: {}, 117 | 118 | /** 119 | * Add a suffix to the request url, e.g. ".json". 120 | * 121 | * @name HttpAdapter#suffix 122 | * @type {string} 123 | * @since 3.0.0 124 | */ 125 | suffix: '', 126 | 127 | /** 128 | * Use `window.fetch` if available. 129 | * 130 | * @name HttpAdapter#useFetch 131 | * @type {boolean} 132 | * @default false 133 | * @since 3.0.0 134 | * @see http://www.js-data.io/docs/js-data-http#using-windowfetch 135 | */ 136 | useFetch: false 137 | } 138 | 139 | /** 140 | * HttpAdapter class. 141 | * 142 | * @example 143 | * import { DataStore } from 'js-data'; 144 | * import { HttpAdapter } from 'js-data-http'; 145 | * 146 | * const httpAdapter = new HttpAdapter(); 147 | * const store = new DataStore(); 148 | * 149 | * store.registerAdapter('http', httpAdapter, { 'default': true }); 150 | * 151 | * store.defineMapper('school'); 152 | * store.defineMapper('student'); 153 | * 154 | * // GET /school/1 155 | * store.find('school', 1).then((school) => { 156 | * console.log('school'); 157 | * }); 158 | * 159 | * @class HttpAdapter 160 | * @extends Adapter 161 | * @param {object} [opts] Configuration options. 162 | * @param {string} [opts.basePath=''] See {@link HttpAdapter#basePath}. 163 | * @param {boolean} [opts.debug=false] See {@link HttpAdapter#debug}. 164 | * @param {boolean} [opts.forceTrailingSlash=false] See {@link HttpAdapter#forceTrailingSlash}. 165 | * @param {object} [opts.http=axios] See {@link HttpAdapter#http}. 166 | * @param {object} [opts.httpConfig={}] See {@link HttpAdapter#httpConfig}. 167 | * @param {string} [opts.suffix=''] See {@link HttpAdapter#suffix}. 168 | * @param {boolean} [opts.useFetch=false] See {@link HttpAdapter#useFetch}. 169 | * @see http://www.js-data.io/docs/js-data-http 170 | */ 171 | export function HttpAdapter (opts) { 172 | utils.classCallCheck(this, HttpAdapter) 173 | 174 | opts || (opts = {}) 175 | // Fill in any missing options with the defaults 176 | utils.fillIn(opts, DEFAULTS) 177 | Adapter.call(this, opts) 178 | } 179 | 180 | /** 181 | * @name module:js-data-http.HttpAdapter 182 | * @see HttpAdapter 183 | */ 184 | 185 | Adapter.extend({ 186 | constructor: HttpAdapter, 187 | 188 | /** 189 | * @name HttpAdapter#afterDEL 190 | * @method 191 | * @param {string} url 192 | * @param {object} config 193 | * @param {object} opts 194 | * @param {object} response 195 | */ 196 | afterDEL: noop2, 197 | 198 | /** 199 | * @name HttpAdapter#afterGET 200 | * @method 201 | * @param {string} url 202 | * @param {object} config 203 | * @param {object} opts 204 | * @param {object} response 205 | */ 206 | afterGET: noop2, 207 | 208 | /** 209 | * @name HttpAdapter#afterHTTP 210 | * @method 211 | * @param {object} config 212 | * @param {object} opts 213 | * @param {object} response 214 | */ 215 | afterHTTP: noop2, 216 | 217 | /** 218 | * @name HttpAdapter#afterPOST 219 | * @method 220 | * @param {string} url 221 | * @param {object} data 222 | * @param {object} config 223 | * @param {object} opts 224 | * @param {object} response 225 | */ 226 | afterPOST: noop2, 227 | 228 | /** 229 | * @name HttpAdapter#afterPUT 230 | * @method 231 | * @param {string} url 232 | * @param {object} data 233 | * @param {object} config 234 | * @param {object} opts 235 | * @param {object} response 236 | */ 237 | afterPUT: noop2, 238 | 239 | /** 240 | * @name HttpAdapter#beforeDEL 241 | * @method 242 | * @param {object} url 243 | * @param {object} config 244 | * @param {object} opts 245 | */ 246 | beforeDEL: noop, 247 | 248 | /** 249 | * @name HttpAdapter#beforeGET 250 | * @method 251 | * @param {object} url 252 | * @param {object} config 253 | * @param {object} opts 254 | */ 255 | beforeGET: noop, 256 | 257 | /** 258 | * @name HttpAdapter#beforeHTTP 259 | * @method 260 | * @param {object} config 261 | * @param {object} opts 262 | */ 263 | beforeHTTP: noop, 264 | 265 | /** 266 | * @name HttpAdapter#beforePOST 267 | * @method 268 | * @param {object} url 269 | * @param {object} data 270 | * @param {object} config 271 | * @param {object} opts 272 | */ 273 | beforePOST: noop, 274 | 275 | /** 276 | * @name HttpAdapter#beforePUT 277 | * @method 278 | * @param {object} url 279 | * @param {object} data 280 | * @param {object} config 281 | * @param {object} opts 282 | */ 283 | beforePUT: noop, 284 | 285 | _count (mapper, query, opts) { 286 | return this.GET( 287 | this.getPath('count', mapper, opts.params, opts), 288 | opts 289 | ).then((response) => this._end(mapper, opts, response)) 290 | }, 291 | 292 | _create (mapper, props, opts) { 293 | return this.POST( 294 | this.getPath('create', mapper, props, opts), 295 | this.serialize(mapper, props, opts), 296 | opts 297 | ).then((response) => this._end(mapper, opts, response)) 298 | }, 299 | 300 | _createMany (mapper, props, opts) { 301 | return this.POST( 302 | this.getPath('createMany', mapper, null, opts), 303 | this.serialize(mapper, props, opts), 304 | opts 305 | ).then((response) => this._end(mapper, opts, response)) 306 | }, 307 | 308 | _destroy (mapper, id, opts) { 309 | return this.DEL( 310 | this.getPath('destroy', mapper, id, opts), 311 | opts 312 | ).then((response) => this._end(mapper, opts, response)) 313 | }, 314 | 315 | _destroyAll (mapper, query, opts) { 316 | return this.DEL( 317 | this.getPath('destroyAll', mapper, null, opts), 318 | opts 319 | ).then((response) => this._end(mapper, opts, response)) 320 | }, 321 | 322 | _end (mapper, opts, response) { 323 | return [this.deserialize(mapper, response, opts), response] 324 | }, 325 | 326 | _find (mapper, id, opts) { 327 | return this.GET( 328 | this.getPath('find', mapper, id, opts), 329 | opts 330 | ).then((response) => this._end(mapper, opts, response)) 331 | }, 332 | 333 | _findAll (mapper, query, opts) { 334 | return this.GET( 335 | this.getPath('findAll', mapper, opts.params, opts), 336 | opts 337 | ).then((response) => this._end(mapper, opts, response)) 338 | }, 339 | 340 | _sum (mapper, field, query, opts) { 341 | return this.GET( 342 | this.getPath('sum', mapper, opts.params, opts), 343 | opts 344 | ).then((response) => this._end(mapper, opts, response)) 345 | }, 346 | 347 | _update (mapper, id, props, opts) { 348 | return this.PUT( 349 | this.getPath('update', mapper, id, opts), 350 | this.serialize(mapper, props, opts), 351 | opts 352 | ).then((response) => this._end(mapper, opts, response)) 353 | }, 354 | 355 | _updateAll (mapper, props, query, opts) { 356 | return this.PUT( 357 | this.getPath('updateAll', mapper, null, opts), 358 | this.serialize(mapper, props, opts), 359 | opts 360 | ).then((response) => this._end(mapper, opts, response)) 361 | }, 362 | 363 | _updateMany (mapper, records, opts) { 364 | return this.PUT( 365 | this.getPath('updateMany', mapper, null, opts), 366 | this.serialize(mapper, records, opts), 367 | opts 368 | ).then((response) => this._end(mapper, opts, response)) 369 | }, 370 | 371 | /** 372 | * Retrieve the number of records that match the selection `query`. 373 | * 374 | * @name HttpAdapter#count 375 | * @method 376 | * @param {object} mapper The mapper. 377 | * @param {object} query Selection query. 378 | * @param {object} [opts] Configuration options. 379 | * @param {string} [opts.params] Querystring parameters. 380 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 381 | * @return {Promise} 382 | */ 383 | count (mapper, query, opts) { 384 | query || (query = {}) 385 | opts || (opts = {}) 386 | opts.params = this.getParams(opts) 387 | opts.params.count = true 388 | opts.suffix = this.getSuffix(mapper, opts) 389 | utils.deepMixIn(opts.params, query) 390 | opts.params = this.queryTransform(mapper, opts.params, opts) 391 | return Adapter.prototype.count.call(this, mapper, query, opts) 392 | }, 393 | 394 | /** 395 | * Create a new the record from the provided `props`. 396 | * 397 | * @name HttpAdapter#create 398 | * @method 399 | * @param {object} mapper The mapper. 400 | * @param {object} props Properties to send as the payload. 401 | * @param {object} [opts] Configuration options. 402 | * @param {string} [opts.params] Querystring parameters. 403 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 404 | * @return {Promise} 405 | */ 406 | create (mapper, props, opts) { 407 | opts || (opts = {}) 408 | opts.params = this.getParams(opts) 409 | opts.params = this.queryTransform(mapper, opts.params, opts) 410 | opts.suffix = this.getSuffix(mapper, opts) 411 | return Adapter.prototype.create.call(this, mapper, props, opts) 412 | }, 413 | 414 | /** 415 | * Create multiple new records in batch. 416 | * 417 | * @name HttpAdapter#createMany 418 | * @method 419 | * @param {object} mapper The mapper. 420 | * @param {array} props Array of property objects to send as the payload. 421 | * @param {object} [opts] Configuration options. 422 | * @param {string} [opts.params] Querystring parameters. 423 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 424 | * @return {Promise} 425 | */ 426 | createMany (mapper, props, opts) { 427 | opts || (opts = {}) 428 | opts.params = this.getParams(opts) 429 | opts.params = this.queryTransform(mapper, opts.params, opts) 430 | opts.suffix = this.getSuffix(mapper, opts) 431 | return Adapter.prototype.createMany.call(this, mapper, props, opts) 432 | }, 433 | 434 | /** 435 | * Make an Http request to `url` according to the configuration in `config`. 436 | * 437 | * @name HttpAdapter#DEL 438 | * @method 439 | * @param {string} url Url for the request. 440 | * @param {object} [config] Http configuration that will be passed to 441 | * {@link HttpAdapter#HTTP}. 442 | * @param {object} [opts] Configuration options. 443 | * @return {Promise} 444 | */ 445 | DEL (url, config, opts) { 446 | let op 447 | config || (config = {}) 448 | opts || (opts = {}) 449 | config.url = url || config.url 450 | config.method = config.method || 'delete' 451 | 452 | // beforeDEL lifecycle hook 453 | op = opts.op = 'beforeDEL' 454 | return utils.resolve(this[op](url, config, opts)) 455 | .then((_config) => { 456 | // Allow re-assignment from lifecycle hook 457 | config = _config === undefined ? config : _config 458 | op = opts.op = 'DEL' 459 | this.dbg(op, url, config, opts) 460 | return this.HTTP(config, opts) 461 | }) 462 | .then((response) => { 463 | // afterDEL lifecycle hook 464 | op = opts.op = 'afterDEL' 465 | return utils.resolve(this[op](url, config, opts, response)) 466 | .then((_response) => _response === undefined ? response : _response) 467 | }) 468 | }, 469 | 470 | /** 471 | * Transform the server response object into the payload that will be returned 472 | * to JSData. 473 | * 474 | * @name HttpAdapter#deserialize 475 | * @method 476 | * @param {object} mapper The mapper used for the operation. 477 | * @param {object} response Response object from {@link HttpAdapter#HTTP}. 478 | * @param {object} opts Configuration options. 479 | * @return {(object|array)} Deserialized data. 480 | */ 481 | deserialize (mapper, response, opts) { 482 | opts || (opts = {}) 483 | if (utils.isFunction(opts.deserialize)) { 484 | return opts.deserialize(mapper, response, opts) 485 | } 486 | if (utils.isFunction(mapper.deserialize)) { 487 | return mapper.deserialize(mapper, response, opts) 488 | } 489 | if (response && response.hasOwnProperty('data')) { 490 | return response.data 491 | } 492 | return response 493 | }, 494 | 495 | /** 496 | * Destroy the record with the given primary key. 497 | * 498 | * @name HttpAdapter#destroy 499 | * @method 500 | * @param {object} mapper The mapper. 501 | * @param {(string|number)} id Primary key of the record to destroy. 502 | * @param {object} [opts] Configuration options. 503 | * @param {string} [opts.params] Querystring parameters. 504 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 505 | * @return {Promise} 506 | */ 507 | destroy (mapper, id, opts) { 508 | opts || (opts = {}) 509 | opts.params = this.getParams(opts) 510 | opts.params = this.queryTransform(mapper, opts.params, opts) 511 | opts.suffix = this.getSuffix(mapper, opts) 512 | return Adapter.prototype.destroy.call(this, mapper, id, opts) 513 | }, 514 | 515 | /** 516 | * Destroy the records that match the selection `query`. 517 | * 518 | * @name HttpAdapter#destroyAll 519 | * @method 520 | * @param {object} mapper The mapper. 521 | * @param {object} query Selection query. 522 | * @param {object} [opts] Configuration options. 523 | * @param {string} [opts.params] Querystring parameters. 524 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 525 | * @return {Promise} 526 | */ 527 | destroyAll (mapper, query, opts) { 528 | query || (query = {}) 529 | opts || (opts = {}) 530 | opts.params = this.getParams(opts) 531 | utils.deepMixIn(opts.params, query) 532 | opts.params = this.queryTransform(mapper, opts.params, opts) 533 | opts.suffix = this.getSuffix(mapper, opts) 534 | return Adapter.prototype.destroyAll.call(this, mapper, query, opts) 535 | }, 536 | 537 | /** 538 | * Log an error. 539 | * 540 | * @name HttpAdapter#error 541 | * @method 542 | * @param {...*} [args] Arguments to log. 543 | */ 544 | error (...args) { 545 | if (console) { 546 | console[typeof console.error === 'function' ? 'error' : 'log'](...args) 547 | } 548 | }, 549 | 550 | /** 551 | * Make an Http request using `window.fetch`. 552 | * 553 | * @name HttpAdapter#fetch 554 | * @method 555 | * @param {object} config Request configuration. 556 | * @param {object} config.data Payload for the request. 557 | * @param {string} config.method Http method for the request. 558 | * @param {object} config.headers Headers for the request. 559 | * @param {object} config.params Querystring for the request. 560 | * @param {string} config.url Url for the request. 561 | */ 562 | fetch (config) { 563 | const requestConfig = { 564 | method: config.method, 565 | // turn the plain headers object into the Fetch Headers object 566 | headers: new Headers(config.headers || {}) 567 | } 568 | 569 | if (config.data) { 570 | requestConfig.body = utils.toJson(config.data) 571 | } 572 | 573 | return fetch(buildUrl(config.url, config.params), requestConfig) 574 | .then((response) => { 575 | response.config = { 576 | method: config.method, 577 | url: config.url 578 | } 579 | return response.json() 580 | .then((data) => { 581 | response.data = data 582 | return response 583 | }) 584 | }) 585 | }, 586 | 587 | /** 588 | * Retrieve the record with the given primary key. 589 | * 590 | * @name HttpAdapter#find 591 | * @method 592 | * @param {object} mapper The mapper. 593 | * @param {(string|number)} id Primary key of the record to retrieve. 594 | * @param {object} [opts] Configuration options. 595 | * @param {string} [opts.params] Querystring parameters. 596 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 597 | * @return {Promise} 598 | */ 599 | find (mapper, id, opts) { 600 | opts || (opts = {}) 601 | opts.params = this.getParams(opts) 602 | opts.params = this.queryTransform(mapper, opts.params, opts) 603 | opts.suffix = this.getSuffix(mapper, opts) 604 | return Adapter.prototype.find.call(this, mapper, id, opts) 605 | }, 606 | 607 | /** 608 | * Retrieve the records that match the selection `query`. 609 | * 610 | * @name HttpAdapter#findAll 611 | * @method 612 | * @param {object} mapper The mapper. 613 | * @param {object} query Selection query. 614 | * @param {object} [opts] Configuration options. 615 | * @param {string} [opts.params] Querystring parameters. 616 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 617 | * @return {Promise} 618 | */ 619 | findAll (mapper, query, opts) { 620 | query || (query = {}) 621 | opts || (opts = {}) 622 | opts.params = this.getParams(opts) 623 | opts.suffix = this.getSuffix(mapper, opts) 624 | utils.deepMixIn(opts.params, query) 625 | opts.params = this.queryTransform(mapper, opts.params, opts) 626 | return Adapter.prototype.findAll.call(this, mapper, query, opts) 627 | }, 628 | 629 | /** 630 | * Make a GET request. 631 | * 632 | * @name HttpAdapter#GET 633 | * @method 634 | * @param {string} url The url for the request. 635 | * @param {object} config Request configuration options. 636 | * @param {object} [opts] Configuration options. 637 | * @return {Promise} 638 | */ 639 | GET (url, config, opts) { 640 | let op 641 | config || (config = {}) 642 | opts || (opts = {}) 643 | config.url = url || config.url 644 | config.method = config.method || 'get' 645 | 646 | // beforeGET lifecycle hook 647 | op = opts.op = 'beforeGET' 648 | return utils.resolve(this[op](url, config, opts)) 649 | .then((_config) => { 650 | // Allow re-assignment from lifecycle hook 651 | config = _config === undefined ? config : _config 652 | op = opts.op = 'GET' 653 | this.dbg(op, url, config, opts) 654 | return this.HTTP(config, opts) 655 | }) 656 | .then((response) => { 657 | // afterGET lifecycle hook 658 | op = opts.op = 'afterGET' 659 | return utils.resolve(this[op](url, config, opts, response)) 660 | .then((_response) => _response === undefined ? response : _response) 661 | }) 662 | }, 663 | 664 | /** 665 | * @name HttpAdapter#getEndpoint 666 | * @method 667 | * @param {object} mapper The Mapper. 668 | * @param {*} id The primary key, if any. 669 | * @param {boolean} opts Configuration options. 670 | * @return {string} Full path. 671 | */ 672 | getEndpoint (mapper, id, opts) { 673 | opts || (opts = {}) 674 | opts.params = utils.isUndefined(opts.params) ? {} : opts.params 675 | const relationList = mapper.relationList || [] 676 | let endpoint = utils.isUndefined(opts.endpoint) ? (utils.isUndefined(mapper.endpoint) ? mapper.name : mapper.endpoint) : opts.endpoint 677 | 678 | relationList.forEach((def) => { 679 | if (def.type !== 'belongsTo' || !def.parent) { 680 | return 681 | } 682 | let item 683 | const parentKey = def.foreignKey 684 | const parentDef = def.getRelation() 685 | let parentId = opts.params[parentKey] 686 | 687 | if (parentId === false || !parentKey || !parentDef) { 688 | if (parentId === false) { 689 | delete opts.params[parentKey] 690 | } 691 | return false 692 | } else { 693 | delete opts.params[parentKey] 694 | 695 | if (utils.isObject(id)) { 696 | item = id 697 | } 698 | 699 | if (item) { 700 | parentId = parentId || def.getForeignKey(item) || (def.getLocalField(item) ? utils.get(def.getLocalField(item), parentDef.idAttribute) : null) 701 | } 702 | 703 | if (parentId) { 704 | delete opts.endpoint 705 | const _opts = {} 706 | utils.forOwn(opts, (value, key) => { 707 | _opts[key] = value 708 | }) 709 | utils._(_opts, parentDef) 710 | endpoint = makePath(this.getEndpoint(parentDef, parentId, _opts), parentId, endpoint) 711 | return false 712 | } 713 | } 714 | }) 715 | 716 | return endpoint 717 | }, 718 | 719 | /** 720 | * @name HttpAdapter#getPath 721 | * @method 722 | * @param {string} method The method being executed. 723 | * @param {object} mapper The Mapper. 724 | * @param {(string|number)?} id The primary key, if any. 725 | * @param {object} opts Configuration options. 726 | */ 727 | getPath (method, mapper, id, opts) { 728 | opts || (opts = {}) 729 | const args = [ 730 | opts.basePath === undefined ? (mapper.basePath === undefined ? this.basePath : mapper.basePath) : opts.basePath, 731 | this.getEndpoint(mapper, (utils.isString(id) || utils.isNumber(id) || method === 'create') ? id : null, opts) 732 | ] 733 | if (method === 'find' || method === 'update' || method === 'destroy') { 734 | args.push(id) 735 | } 736 | return makePath.apply(utils, args) 737 | }, 738 | 739 | getParams (opts) { 740 | opts || (opts = {}) 741 | if (opts.params === undefined) { 742 | return {} 743 | } 744 | return utils.copy(opts.params) 745 | }, 746 | 747 | getSuffix (mapper, opts) { 748 | opts || (opts = {}) 749 | if (opts.suffix === undefined) { 750 | if (mapper.suffix === undefined) { 751 | return this.suffix 752 | } 753 | return mapper.suffix 754 | } 755 | return opts.suffix 756 | }, 757 | 758 | /** 759 | * Make an Http request. 760 | * 761 | * @name HttpAdapter#HTTP 762 | * @method 763 | * @param {object} config Request configuration options. 764 | * @param {object} [opts] Configuration options. 765 | * @return {Promise} 766 | */ 767 | HTTP (config, opts) { 768 | const start = new Date() 769 | opts || (opts = {}) 770 | const payload = config.data 771 | const cache = config.cache 772 | const timeout = config.timeout 773 | const params = config.params 774 | config = utils.copy(config, null, null, null, ['data', 'cache', 'timeout', 'params']) // params could have data, cache, timeout 775 | config.params = utils.copy(params) 776 | config = utils.deepMixIn(config, this.httpConfig) 777 | config.data = payload 778 | config.cache = cache 779 | if (timeout !== undefined) { 780 | config.timeout = timeout 781 | } 782 | if (this.forceTrailingSlash && config.url[config.url.length - 1] !== '/') { 783 | config.url += '/' 784 | } 785 | config.method = config.method.toUpperCase() 786 | const suffix = config.suffix || opts.suffix || this.suffix 787 | if (suffix && config.url.substr(config.url.length - suffix.length) !== suffix) { 788 | config.url += suffix 789 | } 790 | 791 | const logResponse = (data) => { 792 | const str = `${start.toUTCString()} - ${config.method.toUpperCase()} ${config.url} - ${data.status} ${(new Date().getTime() - start.getTime())}ms` 793 | if (data.status >= 200 && data.status < 300) { 794 | if (this.log) { 795 | this.dbg(str, data) 796 | } 797 | return data 798 | } else { 799 | if (this.error) { 800 | this.error(`'FAILED: ${str}`, data) 801 | } 802 | return utils.reject(data) 803 | } 804 | } 805 | 806 | if (!this.http) { 807 | if ((this.useFetch || opts.useFetch)) { 808 | if (!hasFetch) { 809 | throw new Error('Attempting to use window.fetch, but it is not available!') 810 | } 811 | } else { 812 | throw new Error('You have not configured this adapter with an http library!') 813 | } 814 | } 815 | 816 | return utils.resolve(this.beforeHTTP(config, opts)) 817 | .then((_config) => { 818 | config = _config || config 819 | if (hasFetch && (this.useFetch || opts.useFetch || !this.http)) { 820 | return this.fetch(config, opts).then(logResponse, logResponse) 821 | } 822 | const httpConfig = utils.plainCopy(config) 823 | delete httpConfig.adapter 824 | return this.http(httpConfig).then(logResponse, logResponse) 825 | .catch((err) => this.responseError(err, config, opts)) 826 | }) 827 | .then((response) => { 828 | return utils.resolve(this.afterHTTP(config, opts, response)) 829 | .then((_response) => _response === undefined ? response : _response) 830 | }) 831 | }, 832 | 833 | /** 834 | * Make a POST request. 835 | * 836 | * @name HttpAdapter#POST 837 | * @method 838 | * @param {*} url The url for the request. 839 | * @param {object} data Payload for the request. 840 | * @param {object} config Request configuration options. 841 | * @param {object} [opts] Configuration options. 842 | * @return {Promise} 843 | */ 844 | POST (url, data, config, opts) { 845 | let op 846 | config || (config = {}) 847 | opts || (opts = {}) 848 | config.url = url || config.url 849 | config.data = data || config.data 850 | config.method = config.method || 'post' 851 | 852 | // beforePOST lifecycle hook 853 | op = opts.op = 'beforePOST' 854 | return utils.resolve(this[op](url, data, config, opts)) 855 | .then((_config) => { 856 | // Allow re-assignment from lifecycle hook 857 | config = _config === undefined ? config : _config 858 | op = opts.op = 'POST' 859 | this.dbg(op, url, data, config, opts) 860 | return this.HTTP(config, opts) 861 | }) 862 | .then((response) => { 863 | // afterPOST lifecycle hook 864 | op = opts.op = 'afterPOST' 865 | return utils.resolve(this[op](url, data, config, opts, response)) 866 | .then((_response) => _response === undefined ? response : _response) 867 | }) 868 | }, 869 | 870 | /** 871 | * Make a PUT request. 872 | * 873 | * @name HttpAdapter#PUT 874 | * @method 875 | * @param {*} url The url for the request. 876 | * @param {object} data Payload for the request. 877 | * @param {object} config Request configuration options. 878 | * @param {object} [opts] Configuration options. 879 | * @return {Promise} 880 | */ 881 | PUT (url, data, config, opts) { 882 | let op 883 | config || (config = {}) 884 | opts || (opts = {}) 885 | config.url = url || config.url 886 | config.data = data || config.data 887 | config.method = config.method || 'put' 888 | 889 | // beforePUT lifecycle hook 890 | op = opts.op = 'beforePUT' 891 | return utils.resolve(this[op](url, data, config, opts)) 892 | .then((_config) => { 893 | // Allow re-assignment from lifecycle hook 894 | config = _config === undefined ? config : _config 895 | op = opts.op = 'PUT' 896 | this.dbg(op, url, data, config, opts) 897 | return this.HTTP(config, opts) 898 | }) 899 | .then((response) => { 900 | // afterPUT lifecycle hook 901 | op = opts.op = 'afterPUT' 902 | return utils.resolve(this[op](url, data, config, opts, response)) 903 | .then((_response) => _response === undefined ? response : _response) 904 | }) 905 | }, 906 | 907 | /** 908 | * Transform the querystring object before it is serialized. This doesn't do 909 | * anything by default. 910 | * 911 | * @name HttpAdapter#queryTransform 912 | * @method 913 | * @param {object} mapper The Mapper that triggered the request. 914 | * @param {*} params The querystring object. 915 | * @param {*} opts Configuration options 916 | * @return {*} Transformed params. 917 | */ 918 | queryTransform (mapper, params, opts) { 919 | opts || (opts = {}) 920 | if (utils.isFunction(opts.queryTransform)) { 921 | return opts.queryTransform(mapper, params, opts) 922 | } 923 | if (utils.isFunction(mapper.queryTransform)) { 924 | return mapper.queryTransform(mapper, params, opts) 925 | } 926 | return params 927 | }, 928 | 929 | /** 930 | * Error handler invoked when the promise returned by {@link HttpAdapter#http} 931 | * is rejected. Default implementation is to just return the error wrapped in 932 | * a rejected Promise, aka rethrow the error. {@link HttpAdapter#http} is 933 | * called by {@link HttpAdapter#HTTP}. 934 | * 935 | * @name HttpAdapter#responseError 936 | * @method 937 | * @param {*} err The error that {@link HttpAdapter#http} rejected with. 938 | * @param {object} config The `config` argument that was passed to {@link HttpAdapter#HTTP}. 939 | * @param {*} opts The `opts` argument that was passed to {@link HttpAdapter#HTTP}. 940 | * @return {Promise} 941 | */ 942 | responseError (err, config, opts) { 943 | return utils.reject(err) 944 | }, 945 | 946 | /** 947 | * Serialize request data. This doesn't do anything by default. 948 | * 949 | * @name HttpAdapter#serialize 950 | * @method 951 | * @param {object} mapper The Mapper that triggered the request. 952 | * @param {object} data The request payload. 953 | * @param {*} opts Configuration options. 954 | * @return {*} Serialized data. 955 | */ 956 | serialize (mapper, data, opts) { 957 | opts || (opts = {}) 958 | if (utils.isFunction(opts.serialize)) { 959 | return opts.serialize(mapper, data, opts) 960 | } 961 | if (utils.isFunction(mapper.serialize)) { 962 | return mapper.serialize(mapper, data, opts) 963 | } 964 | return data 965 | }, 966 | 967 | /** 968 | * Retrieve the sum of the field of the records that match the selection query. 969 | * 970 | * @name HttpAdapter#sum 971 | * @method 972 | * @param {object} mapper The mapper. 973 | * @param {string} field The field to sum. 974 | * @param {object} query Selection query. 975 | * @param {object} [opts] Configuration options. 976 | * @param {string} [opts.params] Querystring parameters. 977 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 978 | * @return {Promise} 979 | */ 980 | sum (mapper, field, query, opts) { 981 | query || (query = {}) 982 | opts || (opts = {}) 983 | if (!utils.isString(field)) { 984 | throw new Error('field must be a string!') 985 | } 986 | opts.params = this.getParams(opts) 987 | opts.params.sum = field 988 | opts.suffix = this.getSuffix(mapper, opts) 989 | utils.deepMixIn(opts.params, query) 990 | opts.params = this.queryTransform(mapper, opts.params, opts) 991 | return Adapter.prototype.sum.call(this, mapper, field, query, opts) 992 | }, 993 | 994 | /** 995 | * Perform an update. Makes a PUT request by default. 996 | * 997 | * @name HttpAdapter#update 998 | * @method 999 | * @param {object} mapper The Mapper for the request. 1000 | * @param {*} id The primary key of the record being updated. 1001 | * @param {*} props The update payload. 1002 | * @param {object} [opts] Configuration options. 1003 | * @return {Promise} 1004 | */ 1005 | update (mapper, id, props, opts) { 1006 | opts || (opts = {}) 1007 | opts.params = this.getParams(opts) 1008 | opts.params = this.queryTransform(mapper, opts.params, opts) 1009 | opts.suffix = this.getSuffix(mapper, opts) 1010 | return Adapter.prototype.update.call(this, mapper, id, props, opts) 1011 | }, 1012 | 1013 | /** 1014 | * Perform an update against records that match the selection query. Makes a 1015 | * PUT request by default. 1016 | * 1017 | * @name HttpAdapter#updateAll 1018 | * @method 1019 | * @param {object} mapper The Mapper for the request. 1020 | * @param {object} props The update payload. 1021 | * @param {object} query The selection query. See {@link http://www.js-data.io/docs/query-syntax}. 1022 | * @param {object} [opts] Configuration options. 1023 | * @return {Promise} 1024 | */ 1025 | updateAll (mapper, props, query, opts) { 1026 | query || (query = {}) 1027 | opts || (opts = {}) 1028 | opts.params = this.getParams(opts) 1029 | utils.deepMixIn(opts.params, query) 1030 | opts.params = this.queryTransform(mapper, opts.params, opts) 1031 | opts.suffix = this.getSuffix(mapper, opts) 1032 | return Adapter.prototype.updateAll.call(this, mapper, props, query, opts) 1033 | }, 1034 | 1035 | /** 1036 | * Update multiple individual records in a batch. 1037 | * 1038 | * @name HttpAdapter#updateMany 1039 | * @method 1040 | * @param {object} mapper The Mapper for the request. 1041 | * @param {array} records Array of property objects to send as the payload. 1042 | * Each must contain the primary key of the record to be updated. 1043 | * @param {object} [opts] Configuration options. 1044 | * @param {string} [opts.params] Querystring parameters. 1045 | * @param {string} [opts.suffix={@link HttpAdapter#suffix}] See {@link HttpAdapter#suffix}. 1046 | * @return {Promise} 1047 | */ 1048 | updateMany (mapper, records, opts) { 1049 | opts || (opts = {}) 1050 | opts.params = this.getParams(opts) 1051 | opts.params = this.queryTransform(mapper, opts.params, opts) 1052 | opts.suffix = this.getSuffix(mapper, opts) 1053 | return Adapter.prototype.updateMany.call(this, mapper, records, opts) 1054 | } 1055 | }) 1056 | 1057 | /** 1058 | * Add an Http actions to a mapper. 1059 | * 1060 | * @example 1061 | * // CommonJS 1062 | * var JSData = require('js-data'); 1063 | * // It is recommended to use DataStore in the browser 1064 | * var DataStore = JSData.DataStore; 1065 | * 1066 | * var JSDataHttp = require('js-data-http'); 1067 | * var HttpAdapter = JSDataHttp.HttpAdapter; 1068 | * var addAction = JSDataHttp.addAction; 1069 | * 1070 | * var httpAdapter = new HttpAdapter(); 1071 | * var store = new DataStore(); 1072 | * 1073 | * store.registerAdapter('http', httpAdapter, { 'default': true }); 1074 | * store.defineMapper('school'); 1075 | * 1076 | * // GET /reports/schools/:school_id/teachers 1077 | * addAction('getTeacherReports', { 1078 | * endpoint: 'reports/schools', 1079 | * pathname: 'teachers', 1080 | * method: 'GET' 1081 | * })(store.getMapper('school')); 1082 | * 1083 | * // /reports/schools/1234/teachers 1084 | * store.getMapper('school').getTeacherReports(1234).then((response) => { 1085 | * // ... 1086 | * }); 1087 | * 1088 | * @name module:js-data-http.addAction 1089 | * @method 1090 | * @param {string} name Name of the new action. 1091 | * @param {object} [opts] Action configuration 1092 | * @param {string} [opts.adapter="http"] The name of the adapter to use. 1093 | * @param {string} [opts.pathname] Set the action's pathname. 1094 | * @param {function} [opts.request] Specify a request handler to be executed 1095 | * before the request is made. 1096 | * @param {function} [opts.response] Specify a response handler to be executed 1097 | * after the response is received. 1098 | * @param {function} [opts.responseError] Specify an error handler to be 1099 | * executed on error. 1100 | * @return {function} Decoration function, which should be passed the mapper to 1101 | * decorate when invoked. 1102 | */ 1103 | export function addAction (name, opts) { 1104 | if (!name || !utils.isString(name)) { 1105 | throw new TypeError('action(name[, opts]): Expected: string, Found: ' + typeof name) 1106 | } 1107 | return function (mapper) { 1108 | if (mapper[name]) { 1109 | throw new Error('action(name[, opts]): ' + name + ' already exists on target!') 1110 | } 1111 | opts.request = opts.request || function (config) { return config } 1112 | opts.response = opts.response || function (response) { return response } 1113 | opts.responseError = opts.responseError || function (err) { return utils.reject(err) } 1114 | mapper[name] = function (id, _opts) { 1115 | _opts = _opts || {} 1116 | if (utils.isObject(id)) { 1117 | _opts = id 1118 | } 1119 | utils.fillIn(_opts, opts) 1120 | let adapter = this.getAdapter(_opts.adapter || this.defaultAdapter || 'http') 1121 | const config = {} 1122 | config.mapper = this.name 1123 | utils.deepMixIn(config, _opts) 1124 | config.method = config.method || 'GET' 1125 | if (typeof _opts.getEndpoint === 'function') { 1126 | config.url = _opts.getEndpoint(this, _opts) 1127 | } else { 1128 | let args = [ 1129 | _opts.basePath || this.basePath || adapter.basePath, 1130 | adapter.getEndpoint(this, id, _opts) 1131 | ] 1132 | if (utils.isSorN(id)) { 1133 | args.push(id) 1134 | } 1135 | args.push(opts.pathname || name) 1136 | config.url = makePath.apply(null, args) 1137 | } 1138 | return utils.resolve(config) 1139 | .then(_opts.request) 1140 | .then((config) => adapter.HTTP(config)) 1141 | .then((data) => { 1142 | if (data && data.config) { 1143 | data.config.mapper = this.name 1144 | } 1145 | return data 1146 | }) 1147 | .then(_opts.response, _opts.responseError) 1148 | } 1149 | return mapper 1150 | } 1151 | } 1152 | 1153 | /** 1154 | * Add multiple Http actions to a mapper. See {@link HttpAdapter.addAction} for 1155 | * action configuration options. 1156 | * 1157 | * @example 1158 | * // CommonJS 1159 | * var JSData = require('js-data'); 1160 | * // It is recommended to use DataStore in the browser 1161 | * var DataStore = JSData.DataStore; 1162 | * 1163 | * var JSDataHttp = require('js-data-http'); 1164 | * var HttpAdapter = JSDataHttp.HttpAdapter; 1165 | * var addActions = JSDataHttp.addActions; 1166 | * 1167 | * var httpAdapter = new HttpAdapter(); 1168 | * var store = new DataStore(); 1169 | * 1170 | * store.registerAdapter('http', httpAdapter, { 'default': true }); 1171 | * store.defineMapper('school'); 1172 | * 1173 | * addActions({ 1174 | * // GET /reports/schools/:school_id/teachers 1175 | * getTeacherReports: { 1176 | * basePath: 'reports/schools', 1177 | * pathname: 'teachers', 1178 | * method: 'GET' 1179 | * } 1180 | * })(store.getMapper('school')); 1181 | * 1182 | * // /reports/schools/1234/teachers 1183 | * store.getMapper('school').getTeacherReports(1234).then((response) => { 1184 | * // ... 1185 | * }); 1186 | * 1187 | * @name module:js-data-http.addActions 1188 | * @method 1189 | * @param {object.} opts Object where the key is an action name 1190 | * and the value is the configuration for the action. 1191 | * @return {function} Decoration function, which should be passed the mapper to 1192 | * decorate when invoked. 1193 | */ 1194 | export function addActions (opts) { 1195 | opts || (opts = {}) 1196 | return function (mapper) { 1197 | utils.forOwn(opts, function (value, key) { 1198 | addAction(key, value)(mapper) 1199 | }) 1200 | return mapper 1201 | } 1202 | } 1203 | 1204 | /** 1205 | * Details of the current version of the `js-data-http` module. 1206 | * 1207 | * @name module:js-data-http.version 1208 | * @type {object} 1209 | * @property {string} version.full The full semver value. 1210 | * @property {number} version.major The major version number. 1211 | * @property {number} version.minor The minor version number. 1212 | * @property {number} version.patch The patch version number. 1213 | * @property {(string|boolean)} version.alpha The alpha version value, 1214 | * otherwise `false` if the current version is not alpha. 1215 | * @property {(string|boolean)} version.beta The beta version value, 1216 | * otherwise `false` if the current version is not beta. 1217 | */ 1218 | export const version = '<%= version %>' 1219 | 1220 | /** 1221 | * Registered as `js-data-http` in NPM and Bower. The build of `js-data-http` 1222 | * that works on Node.js is registered in NPM as `js-data-http-node`. The build 1223 | * of `js-data-http` that does not bundle `axios` is registered in NPM and Bower 1224 | * as `js-data-fetch`. 1225 | * 1226 | * @example Script tag 1227 | * var HttpAdapter = window.JSDataHttp.HttpAdapter; 1228 | * var httpAdapter = new HttpAdapter(); 1229 | * 1230 | * @example CommonJS 1231 | * var HttpAdapter = require('js-data-Http').HttpAdapter; 1232 | * var httpAdapter = new HttpAdapter(); 1233 | * 1234 | * @example ES2015 Modules 1235 | * import { HttpAdapter } from 'js-data-Http'; 1236 | * const httpAdapter = new HttpAdapter(); 1237 | * 1238 | * @example AMD 1239 | * define('myApp', ['js-data-Http'], function (JSDataHttp) { 1240 | * var HttpAdapter = JSDataHttp.HttpAdapter; 1241 | * var httpAdapter = new HttpAdapter(); 1242 | * 1243 | * // ... 1244 | * }); 1245 | * 1246 | * @module js-data-http 1247 | */ 1248 | 1249 | /** 1250 | * Create a subclass of this HttpAdapter: 1251 | * @example HttpAdapter.extend 1252 | * // Normally you would do: import { HttpAdapter } from 'js-data-http'; 1253 | * // or: import { HttpAdapter } from 'js-data-http-node'; 1254 | * const JSDataHttp = require('js-data-http-node'); 1255 | * const { HttpAdapter } = JSDataHttp; 1256 | * console.log('Using JSDataHttp v' + JSDataHttp.version.full); 1257 | * 1258 | * // Extend the class using ES2015 class syntax. 1259 | * class CustomHttpAdapterClass extends HttpAdapter { 1260 | * foo () { return 'bar'; } 1261 | * static beep () { return 'boop'; } 1262 | * } 1263 | * const customHttpAdapter = new CustomHttpAdapterClass(); 1264 | * console.log(customHttpAdapter.foo()); 1265 | * console.log(CustomHttpAdapterClass.beep()); 1266 | * 1267 | * // Extend the class using alternate method. 1268 | * const OtherHttpAdapterClass = HttpAdapter.extend({ 1269 | * foo () { return 'bar'; } 1270 | * }, { 1271 | * beep () { return 'boop'; } 1272 | * }) 1273 | * const otherHttpAdapter = new OtherHttpAdapterClass(); 1274 | * console.log(otherHttpAdapter.foo()); 1275 | * console.log(OtherHttpAdapterClass.beep()); 1276 | * 1277 | * // Extend the class, providing a custom constructor. 1278 | * function AnotherHttpAdapterClass () { 1279 | * HttpAdapter.call(this); 1280 | * this.created_at = new Date().getTime(); 1281 | * } 1282 | * HttpAdapter.extend({ 1283 | * constructor: AnotherHttpAdapterClass, 1284 | * foo () { return 'bar'; } 1285 | * }, { 1286 | * beep () { return 'boop'; } 1287 | * }) 1288 | * const anotherHttpAdapter = new AnotherHttpAdapterClass(); 1289 | * console.log(anotherHttpAdapter.created_at); 1290 | * console.log(anotherHttpAdapter.foo()); 1291 | * console.log(AnotherHttpAdapterClass.beep()); 1292 | * 1293 | * @method HttpAdapter.extend 1294 | * @param {object} [props={}] Properties to add to the prototype of the 1295 | * subclass. 1296 | * @param {object} [props.constructor] Provide a custom constructor function 1297 | * to be used as the subclass itself. 1298 | * @param {object} [classProps={}] Static properties to add to the subclass. 1299 | * @returns {Constructor} Subclass of this HttpAdapter class. 1300 | * @since 3.0.0 1301 | */ 1302 | -------------------------------------------------------------------------------- /test/DEL.test.js: -------------------------------------------------------------------------------- 1 | describe('DEL', function () { 2 | it('should DEL') 3 | }) 4 | -------------------------------------------------------------------------------- /test/GET.test.js: -------------------------------------------------------------------------------- 1 | describe('GET', function () { 2 | it('should GET') 3 | }) 4 | -------------------------------------------------------------------------------- /test/HTTP.test.js: -------------------------------------------------------------------------------- 1 | describe('HTTP', function () { 2 | it('should HTTP') 3 | }) 4 | -------------------------------------------------------------------------------- /test/POST.test.js: -------------------------------------------------------------------------------- 1 | describe('POST', function () { 2 | it('should POST') 3 | }) 4 | -------------------------------------------------------------------------------- /test/PUT.test.js: -------------------------------------------------------------------------------- 1 | describe('PUT', function () { 2 | it('should PUT') 3 | }) 4 | -------------------------------------------------------------------------------- /test/count.test.js: -------------------------------------------------------------------------------- 1 | describe('sum', function () { 2 | it('should include count=true in query_params', function (done) { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"count": 5}') 7 | }, 5) 8 | 9 | Test.adapter.count(Test.Post).then(function (result) { 10 | Test.assert.equal(Test.requests[0].url, 'api/posts?count=true') 11 | done() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/create.test.js: -------------------------------------------------------------------------------- 1 | describe('create', function () { 2 | it('should create', function () { 3 | var Test = this 4 | var post = { 5 | author: 'John', 6 | age: 30 7 | } 8 | 9 | setTimeout(function () { 10 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 11 | }, 30) 12 | 13 | return Test.adapter.create(Test.Post, post) 14 | .then(function (data) { 15 | Test.assert.equal(Test.requests[0].url, 'api/posts') 16 | Test.assert.equal(Test.requests[0].method, 'POST') 17 | Test.assert.equal(Test.requests[0].requestBody, JSON.stringify(post)) 18 | Test.assert.deepEqual(data, Test.p1, 'post should have been created') 19 | 20 | setTimeout(function () { 21 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 22 | }, 30) 23 | 24 | return Test.adapter.create(Test.Post, post, { 25 | basePath: 'api2' 26 | }) 27 | }) 28 | .then(function (data) { 29 | Test.assert.equal(Test.requests[1].url, 'api2/posts') 30 | Test.assert.equal(Test.requests[1].requestBody, JSON.stringify(post)) 31 | Test.assert.deepEqual(data, Test.p1, 'post should have been created') 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /test/createMany.test.js: -------------------------------------------------------------------------------- 1 | describe('createMany', function () { 2 | it('should createMany', function () { 3 | var Test = this 4 | var many = [{ author_id: 2, text: 'bar' }, { author_id: 2, text: 'foo' }] 5 | 6 | setTimeout(function () { 7 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(many)) 8 | }, 5) 9 | 10 | return Test.Post.createMany(many).then(function (result) { 11 | Test.assert.equal(Test.requests[0].url, 'api/posts') 12 | Test.assert.equal(Test.requests[0].method, 'POST') 13 | Test.assert.equal(Test.requests[0].requestBody, JSON.stringify(many)) 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/deserialize.test.js: -------------------------------------------------------------------------------- 1 | describe('deserialize', function () { 2 | it('should deserialize') 3 | }) 4 | -------------------------------------------------------------------------------- /test/destroy.test.js: -------------------------------------------------------------------------------- 1 | describe('destroy', function () { 2 | it('should destroy', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '1') 7 | }, 30) 8 | 9 | return Test.adapter.destroy(Test.Post, 1) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts/1') 13 | Test.assert.equal(Test.requests[0].method, 'DELETE') 14 | Test.assert.deepEqual(data, 1, 'post should have been deleted') 15 | 16 | setTimeout(function () { 17 | Test.requests[1].respond(200, { 'Content-Type': 'text/plain' }, '1') 18 | }, 30) 19 | 20 | return Test.adapter.destroy(Test.Post, 1, { basePath: 'api2' }) 21 | }) 22 | .then(function (data) { 23 | Test.assert.equal(2, Test.requests.length) 24 | Test.assert.equal(Test.requests[1].url, 'api2/posts/1') 25 | Test.assert.equal(Test.requests[1].method, 'DELETE') 26 | Test.assert.deepEqual(data, 1, 'post should have been deleted') 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/destroyAll.test.js: -------------------------------------------------------------------------------- 1 | describe('destroyAll', function () { 2 | it('should destroyAll', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(204) 7 | }, 30) 8 | 9 | return Test.adapter.destroyAll(Test.Post, {}) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts') 13 | Test.assert.equal(Test.requests[0].method, 'DELETE') 14 | Test.assert.equal('', data, 'posts should have been found') 15 | 16 | setTimeout(function () { 17 | Test.requests[1].respond(204) 18 | }, 30) 19 | 20 | return Test.adapter.destroyAll(Test.Post, { 21 | where: { 22 | author: { 23 | '==': 'John' 24 | } 25 | } 26 | }, { basePath: 'api2' }) 27 | }) 28 | .then(function (data) { 29 | Test.assert.equal(2, Test.requests.length) 30 | Test.assert.equal(Test.requests[1].url.indexOf('api2/posts?where='), 0) 31 | Test.assert.equal(Test.requests[1].method, 'DELETE') 32 | Test.assert.equal('', data, 'posts should have been destroyed') 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/fetch.test.js: -------------------------------------------------------------------------------- 1 | describe('fetch', function () { 2 | it.skip('should fetch from a URL', function () { 3 | var Test = this 4 | if (!Test.adapter.hasFetch) { 5 | return 6 | } 7 | if (!Test.TEST_FETCH) { 8 | return 9 | } 10 | 11 | setTimeout(function () { 12 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{}') 13 | }, 300) 14 | 15 | return Test.adapter.fetch({ 16 | method: 'get', 17 | params: { active: true }, 18 | url: '/api/foos' 19 | }).then(function (response) { 20 | var request = Test.requests[0] 21 | Test.assert.equal(request.method, 'GET') 22 | Test.assert.equal(request.url, '/api/foos?active=true') 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/find.test.js: -------------------------------------------------------------------------------- 1 | describe('find', function () { 2 | it('should find', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 7 | }, 30) 8 | 9 | return Test.adapter.find(Test.Post, 1) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts/1') 13 | Test.assert.equal(Test.requests[0].method, 'GET') 14 | Test.assert.deepEqual(data, Test.p1, 'post should have been found') 15 | 16 | setTimeout(function () { 17 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 18 | }, 30) 19 | 20 | return Test.adapter.find(Test.Post, 1, { basePath: 'api2' }) 21 | }) 22 | .then(function (data) { 23 | Test.assert.equal(2, Test.requests.length) 24 | Test.assert.equal(Test.requests[1].url, 'api2/posts/1') 25 | Test.assert.equal(Test.requests[1].method, 'GET') 26 | Test.assert.deepEqual(data, Test.p1, 'post should have been found') 27 | }) 28 | }) 29 | 30 | it('should use default configs', function () { 31 | var Test = this 32 | 33 | Test.adapter.httpConfig.params = { test: 'test' } 34 | Test.adapter.httpConfig.headers = { Authorization: 'test' } 35 | 36 | setTimeout(function () { 37 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 38 | }, 30) 39 | 40 | return Test.adapter.find(Test.Post, 1) 41 | .then(function (data) { 42 | Test.assert.equal(1, Test.requests.length) 43 | Test.assert.equal(Test.requests[0].url, 'api/posts/1?test=test') 44 | Test.assert.equal(Test.requests[0].method, 'GET') 45 | var testHeaders = { 46 | Authorization: 'test', 47 | Accept: 'application/json, text/plain, */*' 48 | } 49 | if (!Test.adapter.isFetch && !Test.adapter.isNode) { 50 | testHeaders['Content-Type'] = 'text/plain;charset=utf-8' 51 | } 52 | Test.assert.deepEqual(testHeaders, Test.requests[0].requestHeaders) 53 | Test.assert.deepEqual(data, Test.p1, 'post should have been found') 54 | 55 | delete Test.adapter.httpConfig.params.test 56 | delete Test.adapter.httpConfig.headers.Authorization 57 | }) 58 | .catch(function (err) { 59 | delete Test.adapter.httpConfig.params.test 60 | delete Test.adapter.httpConfig.headers.Authorization 61 | return Promise.reject(err) 62 | }) 63 | }) 64 | 65 | it('should log errors', function () { 66 | var Test = this 67 | var loggedError 68 | 69 | Test.adapter.error = function (err) { 70 | loggedError = err 71 | } 72 | 73 | setTimeout(function () { 74 | Test.requests[0].respond(404, { 'Content-Type': 'text/plain' }, 'Not Found') 75 | }, 30) 76 | 77 | return Test.adapter.find(Test.Post, 1) 78 | .then(function () { 79 | throw new Error('Should not have succeeded!') 80 | }, function () { 81 | Test.assert.equal(1, Test.requests.length) 82 | Test.assert.equal(Test.requests[0].url, 'api/posts/1') 83 | Test.assert.equal(Test.requests[0].method, 'GET') 84 | Test.assert.isString(loggedError) 85 | Test.assert.isTrue(loggedError.indexOf('api/posts/1') !== -1) 86 | }) 87 | }) 88 | 89 | it('should use suffixes', function () { 90 | var Test = this 91 | 92 | var Thing = new Test.JSData.Mapper({ 93 | name: 'thing', 94 | endpoint: 'things', 95 | suffix: '.xml' 96 | }) 97 | 98 | var otherAdapter = new Test.HttpAdapter({ 99 | suffix: '.json' 100 | }) 101 | otherAdapter.http = Test.adapter.http 102 | 103 | setTimeout(function () { 104 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ id: 1 })) 105 | }, 30) 106 | 107 | return Test.adapter.find(Thing, 1) 108 | .then(function () { 109 | Test.assert.equal(1, Test.requests.length) 110 | Test.assert.equal(Test.requests[0].url, 'things/1.xml') 111 | Test.assert.equal(Test.requests[0].method, 'GET') 112 | 113 | setTimeout(function () { 114 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ id: 1 })) 115 | }, 30) 116 | 117 | return otherAdapter.find(Test.Post, 1) 118 | }).then(function () { 119 | Test.assert.equal(2, Test.requests.length) 120 | Test.assert.equal(Test.requests[1].url, 'api/posts/1.json') 121 | Test.assert.equal(Test.requests[1].method, 'GET') 122 | }) 123 | }) 124 | 125 | it('should work with multiple parents', function () { 126 | var Test = this 127 | var store = new Test.JSData.DataStore({ 128 | mapperDefaults: { 129 | basePath: 'api' 130 | } 131 | }) 132 | store.registerAdapter('http', Test.adapter, { default: true }) 133 | store.defineMapper('user', { 134 | relations: { 135 | hasMany: { 136 | post: { 137 | foreignKey: 'userId', 138 | localField: 'posts' 139 | }, 140 | comment: { 141 | foreignKey: 'userId', 142 | localField: 'comments' 143 | } 144 | } 145 | } 146 | }) 147 | store.defineMapper('post', { 148 | endpoint: 'posts', 149 | relations: { 150 | belongsTo: { 151 | user: { 152 | parent: true, 153 | foreignKey: 'userId', 154 | localField: 'user' 155 | } 156 | }, 157 | hasMany: { 158 | comment: { 159 | localField: 'comments', 160 | foreignKey: 'postId' 161 | } 162 | } 163 | } 164 | }) 165 | store.defineMapper('comment', { 166 | relations: { 167 | belongsTo: { 168 | post: { 169 | parent: true, 170 | foreignKey: 'postId', 171 | localField: 'post' 172 | }, 173 | user: { 174 | parent: true, 175 | foreignKey: 'userId', 176 | localField: 'user' 177 | } 178 | } 179 | } 180 | }) 181 | 182 | var post = { 183 | id: 1, 184 | userId: 10 185 | } 186 | 187 | var comment = { 188 | id: 3, 189 | postId: 1, 190 | userId: 13 191 | } 192 | 193 | var comment2 = { 194 | id: 4, 195 | userId: 7 196 | } 197 | 198 | setTimeout(function () { 199 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(post)) 200 | }, 30) 201 | 202 | return store.find('post', 1, { params: { userId: 10 } }) 203 | .then(function (data) { 204 | Test.assert.equal(1, Test.requests.length) 205 | Test.assert.equal(Test.requests[0].url, 'api/user/10/posts/1') 206 | Test.assert.equal(Test.requests[0].method, 'GET') 207 | Test.assert.objectsEqual(data, post, 'post should have been found') 208 | 209 | setTimeout(function () { 210 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(comment)) 211 | }, 30) 212 | 213 | return store.find('comment', 3, { params: { postId: 1, userId: 13 } }) 214 | }) 215 | .then(function (data) { 216 | Test.assert.equal(2, Test.requests.length) 217 | Test.assert.equal(Test.requests[1].url, 'api/user/13/posts/1/comment/3') 218 | Test.assert.equal(Test.requests[1].method, 'GET') 219 | Test.assert.objectsEqual(data, comment, 'comment should have been found') 220 | 221 | setTimeout(function () { 222 | Test.requests[2].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(comment2)) 223 | }, 30) 224 | 225 | return store.find('comment', 4, { params: { userId: 7 } }) 226 | }) 227 | .then(function (data) { 228 | Test.assert.equal(3, Test.requests.length) 229 | Test.assert.equal(Test.requests[2].url, 'api/user/7/comment/4') 230 | Test.assert.equal(Test.requests[2].method, 'GET') 231 | Test.assert.objectsEqual(data, comment2, 'comment should have been found') 232 | }) 233 | }) 234 | }) 235 | -------------------------------------------------------------------------------- /test/findAll.test.js: -------------------------------------------------------------------------------- 1 | describe('findAll', function () { 2 | it('should findAll', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify([Test.p1])) 7 | }, 30) 8 | 9 | return Test.adapter.findAll(Test.Post, {}) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts') 13 | Test.assert.equal(Test.requests[0].method, 'GET') 14 | Test.assert.deepEqual(data, [Test.p1], 'posts should have been found') 15 | 16 | setTimeout(function () { 17 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify([Test.p1])) 18 | }, 30) 19 | 20 | return Test.adapter.findAll(Test.Post, { 21 | where: { 22 | author: { 23 | '==': 'John' 24 | } 25 | } 26 | }, { basePath: 'api2' }) 27 | }) 28 | .then(function (data) { 29 | Test.assert.equal(2, Test.requests.length) 30 | Test.assert.equal(Test.requests[1].url.indexOf('api2/posts?where='), 0) 31 | Test.assert.equal(Test.requests[1].method, 'GET') 32 | Test.assert.deepEqual(data, [Test.p1], 'posts should have been found') 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/getEndpoint.test.js: -------------------------------------------------------------------------------- 1 | describe('getEndpoint', function () { 2 | it('should getEndpoint') 3 | }) 4 | -------------------------------------------------------------------------------- /test/getPath.test.js: -------------------------------------------------------------------------------- 1 | describe('getPath', function () { 2 | it('should getPath') 3 | }) 4 | -------------------------------------------------------------------------------- /test/queryTransform.test.js: -------------------------------------------------------------------------------- 1 | describe('queryTransform', function () { 2 | it('should queryTransform') 3 | }) 4 | -------------------------------------------------------------------------------- /test/responseError.test.js: -------------------------------------------------------------------------------- 1 | describe('responseError', function () { 2 | it('should responseError') 3 | }) 4 | -------------------------------------------------------------------------------- /test/serialize.test.js: -------------------------------------------------------------------------------- 1 | describe('serialize', function () { 2 | it('should serialize') 3 | }) 4 | -------------------------------------------------------------------------------- /test/static.addAction.test.js: -------------------------------------------------------------------------------- 1 | describe('static addAction', function () { 2 | it('should addAction', function (done) { 3 | var Test = this 4 | var SchoolMapper = Test.store.defineMapper('school', {}) 5 | 6 | // GET async/reports/schools/:school_id/teachers 7 | Test.addAction('getTeacherReportsAsync', { 8 | basePath: 'async/', 9 | endpoint: 'reports/schools', 10 | pathname: 'teachers', 11 | method: 'GET' 12 | })(SchoolMapper) 13 | 14 | setTimeout(function () { 15 | Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') 16 | }, 5) 17 | 18 | SchoolMapper.getTeacherReportsAsync(1234).then(function (response) { 19 | Test.assert.equal(1, Test.requests.length) 20 | Test.assert.equal(Test.requests[0].url, 'async/reports/schools/1234/teachers', 'Add action configures basePath, endpoint and pathname') 21 | Test.assert.equal(Test.requests[0].method, 'GET') 22 | done() 23 | }) 24 | }) 25 | 26 | it('addAction action is callable with params instead of id', function (done) { 27 | var Test = this 28 | var adapter = Test.adapter 29 | var store = new Test.JSData.DataStore() 30 | store.registerAdapter('http', adapter, { default: true }) 31 | var SchoolMapper = store.defineMapper('school', {}) 32 | 33 | // GET async/reports/schools/teachers 34 | Test.addAction('getAllTeacherReportsAsync', { 35 | basePath: 'async/', 36 | endpoint: 'reports/schools', 37 | pathname: 'teachers', 38 | method: 'GET' 39 | })(SchoolMapper) 40 | 41 | setTimeout(function () { 42 | Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') 43 | }, 5) 44 | 45 | // GET async/reports/schools/teachers?= 46 | SchoolMapper.getAllTeacherReportsAsync({ 47 | params: { 48 | subject: 'esperanto' 49 | } 50 | }).then(function (response) { 51 | Test.assert.equal(1, Test.requests.length) 52 | Test.assert.equal(Test.requests[0].url, 'async/reports/schools/teachers?subject=esperanto', 'Add action configures basePath, endpoint, pathname, and querystring') 53 | Test.assert.equal(Test.requests[0].method, 'GET') 54 | done() 55 | }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /test/static.addActions.test.js: -------------------------------------------------------------------------------- 1 | describe('static addActions', function () { 2 | it('should addActions', function (done) { 3 | var Test = this 4 | var SchoolMapper = Test.store.defineMapper('school', {}) 5 | 6 | // GET async/reports/schools/:school_id/teachers 7 | Test.addActions({ 8 | getTeacherReports: { 9 | endpoint: 'reports/schools', 10 | pathname: 'teachers', 11 | method: 'GET' 12 | }, 13 | getStudentReports: { 14 | endpoint: 'reports/schools', 15 | pathname: 'students', 16 | method: 'GET' 17 | } 18 | })(SchoolMapper) 19 | 20 | var asyncTestOne = function (nextTest) { 21 | setTimeout(function () { 22 | Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') 23 | }, 5) 24 | 25 | SchoolMapper.getTeacherReports(1234).then(function (response) { 26 | Test.assert.equal(1, Test.requests.length) 27 | Test.assert.equal(Test.requests[0].url, 'reports/schools/1234/teachers', 'Add action configures basePath, endpoint and pathname') 28 | Test.assert.equal(Test.requests[0].method, 'GET') 29 | nextTest() 30 | }) 31 | } 32 | 33 | var asyncTestTwo = function () { 34 | setTimeout(function () { 35 | Test.requests[1].respond(200, { 'Content-Type': 'text/plain' }, '') 36 | }, 5) 37 | 38 | SchoolMapper.getStudentReports(1234).then(function (response) { 39 | Test.assert.equal(2, Test.requests.length) 40 | Test.assert.equal(Test.requests[1].url, 'reports/schools/1234/students', 'Add action configures basePath, endpoint and pathname') 41 | Test.assert.equal(Test.requests[1].method, 'GET') 42 | done() 43 | }) 44 | } 45 | 46 | asyncTestOne(asyncTestTwo) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/sum.test.js: -------------------------------------------------------------------------------- 1 | describe('sum', function () { 2 | it('should sum= in query_params', function (done) { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"sum": 5}') 7 | }, 5) 8 | 9 | Test.adapter.sum(Test.Post, 'num_views').then(function (result) { 10 | Test.assert.equal(Test.requests[0].url, 'api/posts?sum=num_views') 11 | done() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/update.test.js: -------------------------------------------------------------------------------- 1 | describe('update', function () { 2 | it('should update', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 7 | }, 30) 8 | 9 | return Test.adapter.update(Test.Post, 1, { author: 'John', age: 30 }) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts/1') 13 | Test.assert.equal(Test.requests[0].method, 'PUT') 14 | Test.assert.equal(Test.requests[0].requestBody, JSON.stringify({ author: 'John', age: 30 })) 15 | Test.assert.deepEqual(data, Test.p1, 'post 5 should have been updated') 16 | 17 | setTimeout(function () { 18 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(Test.p1)) 19 | }, 30) 20 | 21 | return Test.adapter.update(Test.Post, 1, { author: 'John', age: 30 }, { basePath: 'api2' }) 22 | }) 23 | .then(function (data) { 24 | Test.assert.equal(2, Test.requests.length) 25 | Test.assert.equal(Test.requests[1].url, 'api2/posts/1') 26 | Test.assert.equal(Test.requests[1].method, 'PUT') 27 | Test.assert.equal(Test.requests[1].requestBody, JSON.stringify({ author: 'John', age: 30 })) 28 | Test.assert.deepEqual(data, Test.p1, 'post 5 should have been updated') 29 | }) 30 | }) 31 | it('should send nested relations', function () { 32 | var Test = this 33 | var JSData = Test.JSData 34 | var store = new JSData.Container() 35 | store.registerAdapter('http', Test.adapter, { default: true }) 36 | store.defineMapper('user', { 37 | relations: { 38 | hasMany: { 39 | post: { 40 | localField: 'posts', 41 | foreignKey: 'userId' 42 | }, 43 | address: { 44 | localField: 'addresses', 45 | foreignKey: 'userId' 46 | } 47 | } 48 | } 49 | }) 50 | store.defineMapper('post', { 51 | relations: { 52 | belongsTo: { 53 | user: { 54 | localField: 'user', 55 | foreignKey: 'userId' 56 | } 57 | } 58 | } 59 | }) 60 | store.defineMapper('address', { 61 | relations: { 62 | belongsTo: { 63 | user: { 64 | localField: 'user', 65 | foreignKey: 'userId' 66 | } 67 | } 68 | } 69 | }) 70 | 71 | setTimeout(function () { 72 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ 73 | id: 1, 74 | posts: [ 75 | { 76 | id: 2, 77 | userId: 1 78 | } 79 | ] 80 | })) 81 | }, 30) 82 | 83 | return store.update('user', 1, { 84 | id: 1, 85 | posts: [ 86 | { 87 | id: 2, 88 | userId: 1 89 | } 90 | ], 91 | addresses: [ 92 | { 93 | id: 3, 94 | userId: 1 95 | } 96 | ] 97 | }, { with: ['posts'] }) 98 | .then(function (data) { 99 | Test.assert.equal(1, Test.requests.length) 100 | Test.assert.equal(Test.requests[0].url, 'user/1') 101 | Test.assert.equal(Test.requests[0].method, 'PUT') 102 | Test.assert.equal(Test.requests[0].requestBody, JSON.stringify({ 103 | id: 1, 104 | posts: [ 105 | { 106 | id: 2, 107 | userId: 1 108 | } 109 | ] 110 | })) 111 | Test.assert.deepEqual(data, { 112 | id: 1, 113 | posts: [ 114 | { 115 | id: 2, 116 | userId: 1 117 | } 118 | ] 119 | }) 120 | }) 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /test/updateAll.test.js: -------------------------------------------------------------------------------- 1 | describe('updateAll', function () { 2 | it('should updateAll', function () { 3 | var Test = this 4 | 5 | setTimeout(function () { 6 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify([Test.p1])) 7 | }, 30) 8 | 9 | return Test.adapter.updateAll(Test.Post, { author: 'John', age: 30 }) 10 | .then(function (data) { 11 | Test.assert.equal(1, Test.requests.length) 12 | Test.assert.equal(Test.requests[0].url, 'api/posts') 13 | Test.assert.equal(Test.requests[0].method, 'PUT') 14 | Test.assert.deepEqual(data, [Test.p1], 'posts should have been updated') 15 | 16 | setTimeout(function () { 17 | Test.requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify([Test.p1])) 18 | }, 30) 19 | 20 | return Test.adapter.updateAll(Test.Post, { author: 'John', age: 30 }, { 21 | where: { 22 | author: { 23 | '==': 'John' 24 | } 25 | } 26 | }, { basePath: 'api2' }) 27 | }) 28 | .then(function (data) { 29 | Test.assert.equal(2, Test.requests.length) 30 | Test.assert.equal(Test.requests[1].url.indexOf('api2/posts?where='), 0) 31 | Test.assert.equal(Test.requests[1].method, 'PUT') 32 | Test.assert.deepEqual(data, [Test.p1], 'posts should have been updated') 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/updateMany.test.js: -------------------------------------------------------------------------------- 1 | describe('createMany', function () { 2 | it('should createMany', function (done) { 3 | var Test = this 4 | var many = [{ author_id: 2, text: 'bar', id: 1 }, { author_id: 2, text: 'foo', id: 2 }] 5 | 6 | setTimeout(function () { 7 | Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(many)) 8 | }, 5) 9 | 10 | Test.Post.updateMany(many).then(function (result) { 11 | Test.assert.equal(Test.requests[0].url, 'api/posts') 12 | Test.assert.equal(Test.requests[0].method, 'PUT') 13 | Test.assert.equal(Test.requests[0].requestBody, JSON.stringify(many)) 14 | done() 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-http", 3 | "version": false, 4 | "main": "./dist/js-data-http.d.ts", 5 | "ambientDependencies": { 6 | "js-data": "npm:js-data", 7 | "js-data-adapter": "npm:js-data-adapter", 8 | "es6-shim": "registry:dt/es6-shim" 9 | } 10 | } 11 | --------------------------------------------------------------------------------