├── .babelrc ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .jshintrc ├── .mailmap ├── .npmignore ├── .nvmrc ├── AUTHORS ├── CHANGELOG.md ├── LICENSE ├── README.md ├── TRANSITION.md ├── bower.json ├── build_examples ├── browserify │ ├── README.md │ ├── app.js │ └── index.html ├── 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 └── webpack_es6_2 │ ├── README.md │ ├── app.js │ ├── index.html │ └── webpack.config.js ├── circle.yml ├── dist ├── js-data-angular.js ├── js-data-angular.js.map ├── js-data-angular.min.js └── js-data-angular.min.map ├── karma.conf.js ├── karma.start.js ├── package.json ├── scripts ├── AUTHORS ├── CONTRIBUTORS ├── authors.js ├── banner.js ├── cleanup.js ├── js-data-http.js └── version.js ├── src └── index.js ├── test ├── adapters │ └── http │ │ ├── create.test.js │ │ ├── destroy.test.js │ │ ├── destroyAll.test.js │ │ ├── find.test.js │ │ ├── findAll.test.js │ │ ├── update.test.js │ │ └── updateAll.test.js └── datastore │ ├── async_methods │ ├── create.test.js │ ├── destroy.test.js │ ├── destroyAll.test.js │ ├── find.test.js │ ├── findAll.test.js │ ├── loadRelations.test.js │ ├── refresh.test.js │ ├── save.test.js │ ├── update.test.js │ └── updateAll.test.js │ └── sync_methods │ ├── bindAll.test.js │ └── bindOne.test.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | [Read the Contributing Guide](http://js-data.io/docs/contributing). 4 | 5 | ## Support 6 | 7 | [Find out how to Get Support](http://js-data.io/docs/support). 8 | 9 | ## Community 10 | 11 | [Explore the Community](http://js-data.io/docs/community). 12 | 13 | ### Have write access? 14 | 15 | To cut a release: 16 | 17 | 1. Checkout master 18 | 1. Bump version in `package.json` appropriately 19 | 1. Run `npm run release` 20 | 1. Update `CHANGELOG.md` appropriately 21 | 1. Commit and push changes, including the `dist/` folder 22 | 1. Make a GitHub release 23 | - set tag name to version 24 | - set release name to version 25 | - set release body to changelog entry for the version 26 | - attach the files in the `dist/` folder 27 | 1. `npm publish .` 28 | -------------------------------------------------------------------------------- /.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 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | coverage/ 18 | doc/ 19 | bower_components/ 20 | 21 | *.iml 22 | .idea/ 23 | 24 | build_examples/r.js/bundle.js 25 | build_examples/browserify/bundle.js 26 | build_examples/webpack/bundle.js 27 | build_examples/webpack_es6/bundle.js 28 | build_examples/webpack_es6_2/bundle.js 29 | .js-data-http.js 30 | .js-data-http/ -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": false, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "asi": true, 7 | "camelcase": true, 8 | "curly": true, 9 | "eqeqeq": true, 10 | "immed": true, 11 | "indent": 2, 12 | "latedef": true, 13 | "newcap": true, 14 | "noarg": true, 15 | "quotmark": "single", 16 | "regexp": true, 17 | "undef": true, 18 | "unused": true, 19 | "strict": false, 20 | "trailing": true, 21 | "smarttabs": true, 22 | "globals": { 23 | "inject": true, 24 | "describe": true, 25 | "it": true, 26 | "beforeEach": true, 27 | "afterEach": true, 28 | "assert": true, 29 | "startInjector": true, 30 | "DS": true, 31 | "fail": true, 32 | "$httpBackend": true, 33 | "console": true, 34 | "require": true, 35 | "module": true, 36 | "exports": true, 37 | "angular": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | InternalFX Bryan 2 | InternalFX Bryan Morris 3 | Jason Dobry Jason Dobry 4 | Kent C. Dodds Kent C. Dodds 5 | Robert Porter Robert P -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | bower.json 3 | *.iml 4 | .idea/ 5 | test/ 6 | coverage/ 7 | bower_components/ 8 | karma* 9 | guide/ 10 | doc/ 11 | webpack.config.js 12 | circle.yml 13 | 14 | .js-data-http.js 15 | .js-data-http/ -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 6 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of js-data-angular project authors. 2 | # 3 | # Names are formatted as: 4 | # Name or Organization 5 | # The email address is not required for organizations. 6 | Andre Deutmeyer 7 | Artemy Tregubenko 8 | Asaf Katz 9 | Chase Noel 10 | Clark Pan 11 | David Tang 12 | Gabo Esquivel 13 | Jason Aden 14 | Jason Dobry 15 | Kent C. Dodds 16 | Loïc Mahieu 17 | Nikolay 18 | Peter Dave Hello 19 | Shai Reznik 20 | thorn0 21 | Tim Chen 22 | Timothy Krell 23 | vollnhals 24 | Wes Cruver 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ##### 3.2.1 - 18 March 2016 2 | 3 | ###### Backwards compatible bug fixes 4 | - Upgraded to js-data-http 2.2.2 5 | 6 | ##### 3.2.0 - 17 March 2016 7 | 8 | ###### Backwards compatible bug fixes 9 | - #347 - BindOne recomputing computed properties 10 | - #353 - jshint or standard? 11 | - #356 - Multiple parents isn't working 12 | 13 | ##### 3.1.1 - 20 September 2015 14 | 15 | Stable Version 3.1.0 16 | 17 | ###### Backwards compatible API changes 18 | - #335 - Calling $q.reject in http interceptor doesn't resolve to calling reject handler in DS.findAll.then 19 | 20 | ###### Other 21 | - Built with js-data-http 2.1.1 22 | - Upgraded dependencies 23 | 24 | ##### 3.0.0 - 02 July 2015 25 | 26 | Stable Version 3.0.0 27 | 28 | ##### 3.0.0-rc.1 - 28 June 2015 29 | 30 | Stable Version 3.0.0-rc.1 31 | 32 | ##### 3.0.0-beta.2 - 19 April 2015 33 | 34 | Added examples of various build setups `./build_examples/` 35 | 36 | ##### 3.0.0-beta.1 - 17 April 2015 37 | 38 | ###### Backwards compatible API changes 39 | - #306 - Keep it DRY 40 | 41 | ###### Other 42 | - #314 - Switch to using peerDependencies 43 | 44 | ##### 2.4.0 - 15 April 2015 45 | 46 | ###### Backwards compatible API changes 47 | - #327 - `bindOne` now updates computed properties on change 48 | 49 | ##### 2.3.0 - 15 April 2015 50 | 51 | ###### Backwards compatible API changes 52 | - #328 - Add support for `js-data-sql` adapter 53 | 54 | ###### Backwards compatible bug fixes 55 | - #328 - Fixed adapter registration 56 | 57 | ##### 2.2.3 - 08 March 2015 58 | 59 | ###### Other 60 | - Converted to ES6, using Babel.js to transpile to ES5. 61 | 62 | ##### 2.2.2 - 04 March 2015 63 | 64 | ###### Backwards compatible bug fixes 65 | - #312 - UMDifying js-data-angular 66 | - #313 - DSHttpAdapter#find/create/update/destroy do not call queryTransform 67 | 68 | ##### 2.2.1 - 25 February 2015 69 | 70 | ###### Backwards compatible bug fixes 71 | - #311 - Does not properly throw error in find() (like other adapters) when the item cannot be found 72 | 73 | ##### 2.2.0 - 24 February 2015 74 | 75 | ###### Backwards compatible API changes 76 | - Added `suffix` option 77 | 78 | ##### 2.1.0 - 04 February 2015 79 | 80 | Now requiring >= js-data 1.1.0 in order to use removeCircular to safely safe cyclic objects 81 | 82 | ##### 2.0.0 - 03 February 2015 83 | 84 | _Note:_ Please see the [js-data CHANGELOG](https://github.com/js-data/js-data/blob/master/CHANGELOG.md). 85 | 86 | ###### Breaking API changes 87 | - Angular module renamed from `angular-data.DS` to `js-data` 88 | - Refactored to be a wrapper for [js-data](https://github.com/js-data/js-data) 89 | - `deserialize` and `serialize` are now properties of `DSHttpAdapter.defaults` 90 | - `deserialize` and `serialize` are now configuration options used solely by the http adapter 91 | - All hooks (`validate`, `afterCreate`, `serialize`, etc.) now take the actual resource definition as the first argument instead of just the name of the resource 92 | - `DSLocalStorageAdapter` is no longer bundled, but is each separate from js-data-angular. 93 | - The API for `bindOne` and `bindAll` has been changed to be more consistent with the rest of the API 94 | - `eagerInject` has not yet been implemented in `js-data`. 95 | 96 | ##### Backwards compatible API changes 97 | - GitHub project renamed to js-data-angular 98 | - GitHub project moved to the js-data organization 99 | 100 | ###### Other 101 | - #199 - Re-implement bindOne & bindAll in js-data-angular (they're missing from js-data) 102 | - #200 - Need to properly trigger digest where angular-data would have before 103 | - Added DSHttpAdapter fallback that uses $http if js-data-http isn't loaded 104 | - Load an es6-style version of `$q` instead of `es6-promises` to maintain `$scope` integration with the promise lifecycle 105 | - Updated dependencies. Now safely making copies of the `options` passed into methods 106 | 107 | ##### 1.0.0 - 04 October 2014 108 | 109 | Stable Version 1.0.0 110 | 111 | ##### 1.0.0-rc.2-1 - 25 September 2014 112 | 113 | ###### Backwards compatible bug fixes 114 | - #191 - Invalid reference in _eject 115 | 116 | ##### 1.0.0-rc.2 - 25 September 2014 117 | 118 | ###### Backwards compatible bug fixes 119 | - #191 - Add "useClass" option to inject, find, findAll, create 120 | - #155 - Allow deserialize and serialize to be configured per-method as well 121 | - #159 - Find which items from collection have changed with lastModified 122 | - #161 - Allow the http method of DSHttpAdapter methods to be configured 123 | - #166 - Add ID Resolver 124 | - #167 - Default params argument of bindAll to empty object 125 | - #169 - Added eager inject and eager eject options 126 | - #170 - Global callbacks 127 | - #171 - "not in" query 128 | - #175 - Explore adding support for collection-based methods to the DSLocalStorageAdapter 129 | - #177 - Allow promises to be returned in lifecycle hooks 130 | - #182 - option to force trailing slash (DSHttpAdapterProvider.defaults.forceTrailingSlash) 131 | - #185 - eagerInject/eagerEject should default to definition's options 132 | - #186 - eagerInject doesn't add the model to the list after HTTP success 133 | - #187 - Proxy some static methods to instance methods 134 | - Made the `notify` option configurable globally and per-resource 135 | 136 | ###### Backwards compatible bug fixes 137 | - #156 - cached findAll pending query doesn't get removed sometimes 138 | - #163 - loadRelations shouldn't try to load a relation if the id for it is missing 139 | - #165 - DS.hasChanges() reports changes after loading relations 140 | 141 | ##### 1.0.0-rc.1 - 03 September 2014 142 | 143 | ###### Backwards compatible API changes 144 | - #142 - Update references on eject (Added `DS.unlinkInverse`) 145 | 146 | ##### 1.0.0-beta.3 - 30 August 2014 147 | 148 | ###### Backwards compatible bug fixes 149 | - Unfroze resource definitions 150 | 151 | ##### 1.0.0-beta.2 - 27 August 2014 152 | 153 | ###### Breaking API changes 154 | - `findBelongsTo` option of `DS.inject` now defaults to `false` 155 | 156 | ###### Backwards compatible API changes 157 | - #132 - Added `findHasMany` option and capability to `DS.inject` 158 | - #133 - Override resource end point in async methods 159 | - #136 - Add a way to force computed properties to be computed. Added `DS.compute`. Added `DSCompute` to prototype of instances. 160 | - #137 - Add `DS.link`, `DS.linkAll`, and `DS.linkInverse`, and added `linkInverse` option to `DS.inject` 161 | 162 | ###### Backwards compatible bug fixes 163 | - #135 - loadrelations loop 164 | - #140 - lastModified changes when loading distant related objects 165 | 166 | ###### Other 167 | - #138 - Performance optimizations 168 | - #139 - Documentation error 169 | 170 | ##### 1.0.0-beta.1 - 23 August 2014 171 | 172 | ###### Backwards compatible API changes 173 | - #40 - Support for nested resource endpoints 174 | - #118, #122 - Multiple relationships to the same model 175 | - #120 - When using DSCacheFactory, allow onExpire to be specified 176 | - #132 - Inject relations for parent resource on create 177 | - #141 - Allow lifecycle hooks to be overridden per method as well 178 | 179 | ###### Backwards compatible bug fixes 180 | - #126 - injectRelations isn't running when injecting an array 181 | 182 | ###### Other 183 | - #121 - Documentation errors 184 | - #123 - Send query params with DS.find() 185 | - #124 - Documentation overhaul 186 | 187 | ##### 0.10.5 - 14 August 2014 188 | 189 | ###### Backwards compatible API changes 190 | - #111 - DSHttpAdapter default $http config 191 | - #114 - Include resourceName in error messages 192 | - #117 - Event system 193 | 194 | ###### Backwards compatible bug fixes 195 | - #113 - FindAll with object as a result 196 | 197 | ##### 0.10.4 - 04 August 2014 198 | 199 | ###### Breaking API changes 200 | - #110 - `DS.refresh` now always returns a promise 201 | 202 | ###### Backwards compatible API changes 203 | - #103 - Add `upsert` option to `DS.create` 204 | - #107 - Computed properties no dependencies 205 | 206 | ###### Backwards compatible bug fixes 207 | - #104 - Only hijack $rootScope digest when Object.observe is unavailable 208 | - #105 - prototype methods shouldn't be included in change sets 209 | - #106 - cacheResponse: false should force bypassCache to true 210 | - #108 - Computed properties array syntax and primary id 211 | - #110 - `DS.refresh` should still return a promise if the item isn't already in the data store 212 | 213 | ##### 0.10.3 - 24 July 2014 214 | 215 | ###### Backwards compatible bug fixes 216 | - #100 - defineResource can't handle names that aren't [a-zA-z0-9] 217 | - #101 - DS.findAll isn't added to completedQueries 218 | - #102 - Resource objects are missing the changes, eject, and ejectAll methods 219 | 220 | ##### 0.10.2 - 21 July 2014 221 | 222 | ###### Backwards compatible bug fixes 223 | - #99 - Computed properties + uglify 224 | 225 | ##### 0.10.1 - 20 July 2014 226 | 227 | ###### Backwards compatible API changes 228 | - #93 - Added `DS.createInstance(resourceName[, attrs][, options])` 229 | - #96 - Resource definitions should be able to proxy DS methods 230 | 231 | ###### Backwards compatible bug fixes 232 | - #90 - DS.create isn't added to completedQueries (`DS.create` now adds a completed query entry) 233 | - #91 - dist/js-data-angular(.min).js doesn't end with a semicolon (upgraded Browserify) 234 | - #94 - Resource object name/class inconsistency (added `useClass` option to `DS.defineResource`) 235 | - #95 - observe-js outdated (Upgraded observe-js.js an refactored to new API) 236 | - #98 - Missing id warning 237 | 238 | ##### 0.10.0 - 18 July 2014 239 | 240 | Official Release 241 | 242 | ##### 0.10.0-beta.2 - 10 July 2014 243 | 244 | ###### Backwards compatible API changes 245 | - #89 - Added the `cacheResponse` option to `DS.create` and `DS.save` 246 | 247 | ###### Backwards compatible bug fixes 248 | - #87 - Filter where boolean values 249 | 250 | ###### Other 251 | - #88 - Fixed guide documentation for the simple default `where` filter 252 | 253 | ##### 0.10.0-beta.1 - 28 June 2014 254 | 255 | ###### Breaking API changes 256 | - #76 - Queries and filtering. See [TRANSITION.md](https://github.com/js-data/js-data-angular/blob/master/TRANSITION.md). 257 | - #82 - Simplify error handling. Reduced size of js-data-angular.min.js by 4kb. 258 | - #42 - Relations/Associations. `DS.inject` now looks for relations and injects them as well. 259 | 260 | ###### Backwards compatible API changes 261 | - #17 - Where predicates should be able to handle OR, not just AND 262 | - #23 - Computed Properties 263 | - #78 - Added optional callback to `bindOne` and `bindAll` 264 | - #79 - `ejectAll` should clear matching completed queries 265 | - #83 - Implement `DS.loadRelations(resourceName, instance(Id), relations[, options])` 266 | - #84 - idAttribute of a resource can be a computed property 267 | 268 | ##### 0.9.1 - 30 May 2014 269 | 270 | ###### Backwards compatible bug fixes 271 | - #68 - Async methods should honor methods on a resource definition. 272 | - #69 - Failed requests should throw an error, not just return it 273 | - #70 - Make `params` and `params.query` optional 274 | 275 | ##### 0.9.0 - 22 May 2014 276 | 277 | ###### Breaking API changes 278 | - #61 - Make custom serializers/deserializers more valuable 279 | - #59, #62 - Make queryTransform() consistent with the rest of the API 280 | 281 | __Before:__ 282 | 283 | ```js 284 | DSHttpAdapterProvider.defaults.serialize = function (data) { ... }; 285 | ``` 286 | 287 | __After:__ 288 | 289 | ```js 290 | DSProvider.defaults.serialize = function (resourceName, data) { ... }; 291 | ``` 292 | 293 | __Before:__ 294 | 295 | ```js 296 | DSHttpAdapterProvider.defaults.deserialize = function (data) { ... }; 297 | ``` 298 | 299 | __After:__ 300 | 301 | ```js 302 | DSProvider.defaults.deserialize = function (resourceName, data) { ... }; 303 | ``` 304 | 305 | __Before:__ 306 | 307 | ```js 308 | DSHttpAdapterProvider.defaults.queryTransform = function (query) { ... }; 309 | ``` 310 | 311 | __After:__ 312 | 313 | ```js 314 | DSHttpAdapterProvider.defaults.queryTransform = function (resourceName, query) { ... }; 315 | ``` 316 | 317 | ###### Backwards compatible API changes 318 | - #30, #48, #66 - DSCacheFactory integration 319 | - #49 - DS.bindOne($scope, prop, resourceName, id) 320 | - #50 - DS.bindAll($scope, prop, resourceName, query) 321 | - #51 - Allow baseUrl to be overridable at the method level 322 | - #52 - DS.update(resourceName, id, attrs[, options]) (different from DS.save()) 323 | - #54 - Adding functionality to resources 324 | - #56 - DS.updateAll(resourceName, attrs, params[, options]) 325 | - #60 - DSHttpAdapterProvider.defaults is undefined 326 | - #63 - DSLocalStorageAdapter 327 | 328 | ##### 0.8.1 - 02 May 2014 329 | 330 | ###### Backwards compatible Bug fixes 331 | - #44 - Pending query isn't deleted when the response is a failure 332 | - #47 - Minification error in $q $delegate 333 | 334 | ##### 0.8.0 - 13 March 2014 335 | 336 | ###### Backwards compatible API changes 337 | - #37 - Add option to only save changed attributes when calling DS.save() 338 | 339 | ###### Backwards compatible bug fixes 340 | - #38 - "saved" attribute item isn't being updated properly 341 | 342 | ##### 0.7.1 - 26 February 2014 343 | 344 | ###### Backwards compatible bug fixes 345 | - #36 - Fixed inconsistencies in `DS.filter` when using skip or limit in the query 346 | 347 | ##### 0.7.0 - 24 February 2014 348 | 349 | ###### Breaking API changes 350 | - `DS.eject(resourceName, id)` can now only eject individual items 351 | 352 | ###### Backwards compatible API changes 353 | - #34 - Added `DS.ejectAll(resourceName, params)` 354 | - #33 - Added `DS.destroyAll(resourceName, params[, options])` 355 | - #35 - Add options for asynchronous getter methods to return data without putting it into the data store 356 | 357 | ##### 0.6.0 - 17 January 2014 358 | 359 | Added pluggable filters for the "where" clause of a query. Angular-data's "query language" will remain small and simple. 360 | Developers can provide their own more robust filters for more powerful queries. 361 | 362 | ###### Breaking API changes 363 | - #3 - Pluggable async adapters 364 | 365 | ###### Backwards API changes 366 | - #2 - js-data-angular query language 367 | - #4 - Query caching 368 | - #17 - Where predicates should be able to handle OR, not just AND 369 | - #22 - Reorganize infrastructure to utilize angular's DI 370 | 371 | ##### 0.5.0 - 16 January 2014 372 | 373 | ###### Backwards API changes 374 | - #1 - Pluggable adapters 375 | - #6 - Observable objects 376 | - #7 - Model lifecycle hooks 377 | 378 | ###### Backwards compatible bug fixes 379 | - #19 - Null pointer exception in several places where js-data-angular tries to use the $q service 380 | 381 | ##### Other 382 | - #15 - Integration test coverage 383 | 384 | ##### 0.4.2 - 15 January 2014 385 | 386 | ###### Backwards compatible bug fixes 387 | - #18 - observers aren't created for items injected into the store from a findAll() call. 388 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 js-data-angular project authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | js-data logo 2 | 3 | ## js-data-angular [![bower version](https://img.shields.io/bower/v/js-data-angular.svg?style=flat-square)](https://www.npmjs.org/package/js-data-angular) [![npm version](https://img.shields.io/npm/v/js-data-angular.svg?style=flat-square)](https://www.npmjs.org/package/js-data-angular) [![Circle CI](https://img.shields.io/circleci/project/js-data/js-data-angular/master.svg?style=flat-square)](https://circleci.com/gh/js-data/js-data-angular/tree/master) [![npm downloads](https://img.shields.io/npm/dm/js-data-angular.svg?style=flat-square)](https://www.npmjs.org/package/js-data-angular) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/js-data/js-data-angular/blob/master/LICENSE) 4 | 5 | Angular wrapper for [js-data](http://www.js-data.io). 6 | 7 | #### What happened Angular-data? 8 | Angular-data is deprecated. js-data + js-data-angular is the new hotness. 9 | 10 | ### Guides 11 | - [Angular + JSData (js-data-angular)](http://www.js-data.io/docs/js-data-angular) 12 | - [Getting Started with js-data](http://www.js-data.io/docs/home) 13 | - [Resources/Models](http://www.js-data.io/docs/resources) 14 | - [Working with the Data Store](http://www.js-data.io/docs/working-with-the-data-store) 15 | - [Adapters](http://www.js-data.io/docs/working-with-adapters) 16 | - [Query Syntax](http://www.js-data.io/docs/query-syntax) 17 | - [Model Lifecycle](http://www.js-data.io/docs/model-lifecycle) 18 | - [Custom Instance Behavior](http://www.js-data.io/docs/custom-instance-behavior) 19 | - [Computed Properties](http://www.js-data.io/docs/computed-properties) 20 | - [Relations](http://www.js-data.io/docs/relations) 21 | - [Schemata & Validation](http://www.js-data.io/docs/schemata--validation) 22 | - [JSData on the Server](http://www.js-data.io/docs/jsdata-on-the-server) 23 | - [FAQ](http://www.js-data.io/docs/faq) 24 | 25 | ### Js-data-angular API Documentation 26 | - [js-data-angular](http://www.js-data.io/docs/js-data-angular) 27 | - [DS](http://www.js-data.io/docs/ds) 28 | - [js-data-schema](http://www.js-data.io/docs/js-data-schema) 29 | - [DSHttpAdapter](http://www.js-data.io/docs/dshttpadapter) 30 | - [DSLocalStorageAdapter](http://www.js-data.io/docs/dslocalstorageadapter) 31 | - [DSLocalForageAdapter](http://www.js-data.io/docs/dslocalforageadapter) 32 | - [DSFirebaseAdapter](http://www.js-data.io/docs/dsfirebaseadapter) 33 | - [DSRedisAdapter](http://www.js-data.io/docs/dsredisadapter) 34 | - [DSRethinkDBAdapter](http://www.js-data.io/docs/dsrethinkdbadapter) 35 | - [DSMongoDBAdapter](http://www.js-data.io/docs/dsmongodbadapter) 36 | - [DSSqlAdapter](http://www.js-data.io/docs/dssqladapter) 37 | 38 | ### Project Status 39 | 40 | __Latest Release:__ [![Latest Release](https://img.shields.io/github/release/js-data/js-data-angular.svg?style=flat-square)](https://github.com/js-data/js-data-angular/releases) 41 | 42 | __Status:__ 43 | 44 | [![Dependency Status](https://img.shields.io/gemnasium/js-data/js-data-angular.svg?style=flat-square)](https://gemnasium.com/js-data/js-data-angular) [![Coverage Status](https://img.shields.io/coveralls/js-data/js-data-angular/master.svg?style=flat-square)](https://coveralls.io/r/js-data/js-data-angular?branch=master) [![Codacity](https://img.shields.io/codacy/e7690b906dfa471ebcc8b2bdc52e9662.svg?style=flat-square)](https://www.codacy.com/public/jasondobry/js-data-angular/dashboard) 45 | 46 | __Supported Platforms:__ 47 | 48 | [![browsers](https://img.shields.io/badge/Browser-Chrome%2CFirefox%2CSafari%2COpera%2CIE%209%2B%2CiOS%20Safari%207.1%2B%2CAndroid%20Browser%202.3%2B-green.svg?style=flat-square)](https://github.com/js-data/js-data) 49 | 50 | ### Quick Start 51 | `bower install --save js-data js-data-angular` or `npm install --save js-data js-data-angular`. 52 | 53 | Load `js-data-angular.js` after `js-data.js`. 54 | 55 | ```js 56 | angular.module('myApp', ['js-data']); 57 | ``` 58 | 59 | ```js 60 | angular.module('myApp').factory('Post', function (DS) { 61 | return DS.defineResource('post'); 62 | }); 63 | angular.module('myApp').factory('Comment', function (DS) { 64 | return DS.defineResource('comment'); 65 | }); 66 | ``` 67 | 68 | ```js 69 | app.controller('postCtrl', function ($scope, $routeParams, Post, Comment) { 70 | // it's up to your server to know how to interpret this query 71 | // or you can teach js-data how to understand your servers' query language 72 | var query = { 73 | postId: $routeParams.id 74 | }; 75 | 76 | Post.find($routeParams.id); 77 | Comment.findAll(query); 78 | 79 | // My goodness this was easy 80 | Post.bindOne($routeParams.id, $scope, 'post'); 81 | Comment.bindAll(query, $scope, 'comments'); 82 | 83 | // Long form (same effect as above) 84 | $scope.$watch(function () { 85 | return Post.lastModified($routeParams.id); 86 | }, function () { 87 | $scope.post = Post.get($routeParams.id); 88 | }); 89 | $scope.$watch(function () { 90 | // Changes when anything in the Comment collection is modified 91 | return Comment.lastModified(); 92 | }, function () { 93 | $scope.comments = Comment.filter(query); 94 | }); 95 | }); 96 | ``` 97 | 98 | ### Changelog 99 | [CHANGELOG.md](https://github.com/js-data/js-data-angular/blob/master/CHANGELOG.md) 100 | 101 | ### Community 102 | - [Slack Room](http://slack.js-data.io) - Better than IRC! 103 | - [Announcements](http://www.js-data.io/blog) 104 | - [Mailing List](https://groups.io/org/groupsio/jsdata) - Ask your questions! 105 | - [Issues](https://github.com/js-data/js-data-angular/issues) - Found a bug? Feature request? Submit an issue! 106 | - [GitHub](https://github.com/js-data/js-data-angular) - View the source code for js-data. 107 | - [Contributing Guide](https://github.com/js-data/js-data-angular/blob/master/CONTRIBUTING.md) 108 | 109 | ### Contributing 110 | 111 | First, support is handled via the [Gitter Channel](https://gitter.im/js-data/js-data) and the [Mailing List](https://groups.io/org/groupsio/jsdata). Ask your questions there. 112 | 113 | When submitting issues on GitHub, please include as much detail as possible to make debugging quick and easy. 114 | 115 | - good - Your versions of Angular, js-data, js-data-angular, etc., relevant console logs/error, code examples that revealed the issue 116 | - better - A [plnkr](http://plnkr.co/), [fiddle](http://jsfiddle.net/), or [bin](http://jsbin.com/?html,output) that demonstrates the issue 117 | - best - A Pull Request that fixes the issue, including test coverage for the issue and the fix 118 | 119 | [Github Issues](https://github.com/js-data/js-data-angular/issues). 120 | 121 | #### Submitting Pull Requests 122 | 123 | 1. Contribute to the issue/discussion that is the reason you'll be developing in the first place 124 | 1. Fork js-data-angular 125 | 1. `git clone git@github.com:/js-data-angular.git` 126 | 1. `cd js-data-angular; npm install; bower install;` 127 | 1. Write your code, including relevant documentation and tests 128 | 1. Run `grunt test` (build and test) 129 | 1. Your code will be linted and checked for formatting, the tests will be run 130 | 1. The `dist/` folder & files will be generated, do NOT commit `dist/*`! They will be committed when a release is cut. 131 | 1. Submit your PR and we'll review! 132 | 1. Thanks! 133 | 134 | ### License 135 | 136 | The MIT License (MIT) 137 | 138 | Copyright (c) 2014-2015 Jason Dobry 139 | 140 | Permission is hereby granted, free of charge, to any person obtaining a copy 141 | of this software and associated documentation files (the "Software"), to deal 142 | in the Software without restriction, including without limitation the rights 143 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 144 | copies of the Software, and to permit persons to whom the Software is 145 | furnished to do so, subject to the following conditions: 146 | 147 | The above copyright notice and this permission notice shall be included in all 148 | copies or substantial portions of the Software. 149 | 150 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 151 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 152 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 153 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 154 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 155 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 156 | SOFTWARE. 157 | 158 | -------------------------------------------------------------------------------- /TRANSITION.md: -------------------------------------------------------------------------------- 1 | ### 1.x ---> 2.x - 03 February 2015 2 | 3 | #### Breaking API changes 4 | 5 | ##### Module name change 6 | 7 | ###### Before 8 | `angular.module('myApp', ['angular-data.DS'])` 9 | 10 | ###### After 11 | `angular.module('myApp', ['js-data'])` 12 | 13 | ##### `bindOne` and `bindAll` syntax change 14 | 15 | ###### Before 16 | [bindOne](http://angular-data.pseudobry.com/documentation/api/angular-data/DS.sync%20methods_bindOne) and [bindAll](http://angular-data.pseudobry.com/documentation/api/angular-data/DS.sync%20methods_bindAll) 17 | 18 | ###### After 19 | [bindOne](http://www.js-data.io/docs/js-data-angular#dsbindone) and [bindAll](http://www.js-data.io/docs/js-data-angular#dsbindall) 20 | 21 | #### Backwards compatible API changes 22 | 23 | ##### Repo re-assignment and name change 24 | 25 | ###### Before 26 | `https://github.com/jmdobry/angular-data.git` 27 | 28 | ###### After 29 | `https://github.com/js-data/js-data-angular.git` 30 | 31 | ##### New Bower package 32 | 33 | ###### Before 34 | `bower install --save angular-data` 35 | 36 | ###### After 37 | `bower install --save js-data-angular` 38 | 39 | ##### New NPM package 40 | 41 | ###### Before 42 | `npm install --save angular-data` 43 | 44 | ###### After 45 | `npm install --save js-data js-data-angular` 46 | 47 | ### 0.9.x. ---> 0.10.x - 29 June 2014 48 | 49 | #### Breaking API changes 50 | ##### #76 - Queries and filtering. 51 | 52 | ###### Before 53 | `IllegalArgumentError` has an `errors` field. 54 | 55 | ###### After 56 | `IllegalArgumentError` no longer has an `errors` field. 57 | 58 | ###### Before 59 | ```javascript 60 | DS.findAll('post', { 61 | query: { 62 | where: { 63 | name: 'John' 64 | } 65 | } 66 | }) 67 | ``` 68 | 69 | ###### After 70 | ```javascript 71 | DS.findAll('post', { 72 | where: { 73 | name: 'John' 74 | } 75 | }) 76 | ``` 77 | 78 | ###### Before 79 | ```javascript 80 | DS.filter('post', { 81 | query: { 82 | where: { 83 | name: 'John' 84 | } 85 | } 86 | }) 87 | ``` 88 | 89 | ###### After 90 | ```javascript 91 | DS.filter('post', { 92 | where: { 93 | name: 'John' 94 | } 95 | }) 96 | ``` 97 | 98 | ###### Before 99 | ```javascript 100 | // override how DS.filter handles the "where" clause 101 | DSProvider.defaults.filter = function (resourceName, where, attrs) { 102 | // return true to keep the item in the result set 103 | // return false to exclude it 104 | }; 105 | ``` 106 | 107 | ###### After 108 | ```javascript 109 | // override how DS.filter handles the "where", "skip", "limit" and "orderBy" clauses 110 | DSProvider.defaults.filter = function (collection, resourceName, params, options) { 111 | // examine params and 112 | // decide whether to exclude items, skip items, start from an offset, or sort the items 113 | 114 | // see the [default implementation that ships with js-data-angular](https://github.com/js-data/js-data-angular/blob/master/src/datastore/index.js#L12) 115 | // overriding this method is useful when our server only understands a certain 116 | // params format and you want js-data-angular's filter to behave the same as your server 117 | 118 | // js-data-angular looks for the following fields: 119 | // - where 120 | // - skip (or offset) 121 | // - limit 122 | // - orderBy (or sort) 123 | 124 | // return the filtered collection 125 | }; 126 | ``` 127 | 128 | ###### Before 129 | ```javascript 130 | DSHttpAdapter.defaults.queryTransform = function (resourceName, query) { 131 | // the second argument was the "query" field of the "params" passed in the DSHttpAdapter method 132 | // return the transformed query 133 | }; 134 | ``` 135 | 136 | ###### After 137 | ```javascript 138 | // This is useful when you don't want to implement the filter method above 139 | // and instead rely on js-data-angular's expectation of where, skip, limit, orderBy, etc. 140 | // This transform is useful when you want to change the where, skip, limit, orderBy, etc. fields 141 | // into something your server understands. 142 | DSHttpAdapter.defaults.queryTransform = function (resourceName, params) { 143 | // the second argument is now the whole "params" object passed in the DSHttpAdapter method 144 | // return the transformed params 145 | }; 146 | ``` 147 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "js-data-angular project authors", 3 | "name": "js-data-angular", 4 | "description": "Angular wrapper for js-data (originally angular-data).", 5 | "homepage": "https://github.com/js-data/js-data-angular", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-angular.git" 9 | }, 10 | "main": "./dist/js-data-angular.js", 11 | "ignore": [ 12 | ".idea/", 13 | ".*", 14 | "*.iml", 15 | "src/", 16 | "node_modules/", 17 | "doc", 18 | "coverage", 19 | "test", 20 | "package.json" 21 | ], 22 | "devDependencies": { 23 | "angular-1.3.2": "angular#1.3.2", 24 | "angular-mocks-1.3.2": "angular-mocks#1.3.2" 25 | }, 26 | "dependencies": { 27 | "js-data": ">=2.0.0 <3", 28 | "angular": ">=1.1.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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 | // this is what you would do in a real app 2 | // var angular = require('angular'); 3 | 4 | // for the example to work 5 | var angular = require('../../node_modules/angular'); 6 | 7 | console.log(angular); 8 | 9 | angular.module('app', [ 10 | // this is what you would do in a real app 11 | // require('js-data-angular') 12 | 13 | // for the example to work 14 | require('../../dist/js-data-angular.js') 15 | ]).run(function (DS, DSVersion, $rootScope) { 16 | $rootScope.test = 'It works! Using js-data ' + DSVersion.full; 17 | }); 18 | -------------------------------------------------------------------------------- /build_examples/browserify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 | 8 | 9 |

{{ test }}

10 | 11 | 12 | -------------------------------------------------------------------------------- /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 | 'angular', 3 | 'js-data-angular' 4 | ], function (angular, jsDataModuleName) { 5 | return angular.module('app', [jsDataModuleName]) 6 | .run(function (DS, DSVersion, $rootScope) { 7 | $rootScope.test = 'It works! Using js-data ' + DSVersion.full; 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /build_examples/r.js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

{{ test }}

13 | 14 | 15 | -------------------------------------------------------------------------------- /build_examples/r.js/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | paths: { 3 | angular: '../../bower_components/angular/angular', 4 | 'js-data-angular': '../../dist/js-data-angular', 5 | 'js-data': '../../bower_components/js-data/dist/js-data' 6 | }, 7 | shim: { 8 | 'angular': { 9 | exports: 'angular' 10 | } 11 | } 12 | }); 13 | 14 | require([ 15 | 'angular', 16 | 'app' 17 | ], function (angular, app) { 18 | angular.element(document.getElementsByTagName('html')[0]).ready(function () { 19 | // bootstrap the app manually 20 | angular.bootstrap(document, ['app']); 21 | }); 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /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 | // this is what you would do in a real app 2 | // var angular = require('angular'); 3 | 4 | // for the example to work 5 | var angular = require('../../node_modules/angular'); 6 | 7 | console.log(angular); 8 | 9 | angular.module('app', [ 10 | // this is what you would do in a real app 11 | // require('js-data-angular') 12 | 13 | // for the example to work 14 | require('../../dist/js-data-angular.js') 15 | ]).run(function (DS, DSVersion, $rootScope) { 16 | $rootScope.test = 'It works! Using js-data ' + DSVersion.full; 17 | }); 18 | -------------------------------------------------------------------------------- /build_examples/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 | 8 | 9 |

{{ test }}

10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './app.js', 3 | output: { 4 | filename: 'bundle.js' 5 | }, 6 | externals: ['axios'], 7 | resolve: { 8 | alias: { 9 | 'js-data-angular': '../dist/js-data-angular.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 angular from 'angular'; 2 | import jsDataModuleName from 'js-data-angular'; 3 | 4 | angular.module('app', [ 5 | jsDataModuleName 6 | ]).run((DS, DSVersion, $rootScope) => { 7 | $rootScope.test = 'It works! Using js-data ' + DSVersion.full; 8 | }); 9 | -------------------------------------------------------------------------------- /build_examples/webpack_es6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 | 8 | 9 |

{{ test }}

10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/webpack_es6/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './app.js', 3 | output: { 4 | filename: 'bundle.js' 5 | }, 6 | externals: ['axios'], 7 | resolve: { 8 | alias: { 9 | 'js-data-angular': '../../dist/js-data-angular.js' 10 | } 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /(.+)\.js$/, loader: 'babel-loader?blacklist=useStrict' } 15 | ] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /build_examples/webpack_es6_2/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_2/app.js: -------------------------------------------------------------------------------- 1 | import 'angular'; 2 | import 'js-data-angular'; 3 | 4 | angular.module('app', [ 5 | 'js-data' 6 | ]).run((DS, DSVersion, $rootScope) => { 7 | $rootScope.test = 'It works! Using js-data ' + DSVersion.full; 8 | }); 9 | -------------------------------------------------------------------------------- /build_examples/webpack_es6_2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My App 5 | 6 | 7 | 8 | 9 |

{{ test }}

10 | 11 | 12 | -------------------------------------------------------------------------------- /build_examples/webpack_es6_2/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './app.js', 3 | output: { 4 | filename: 'bundle.js' 5 | }, 6 | externals: ['axios'], 7 | resolve: { 8 | alias: { 9 | 'js-data-angular': '../../dist/js-data-angular.js' 10 | } 11 | }, 12 | module: { 13 | loaders: [ 14 | { test: /(.+)\.js$/, loader: 'babel-loader?blacklist=useStrict' } 15 | ] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | general: 2 | branches: 3 | ignore: 4 | - gh-pages # list of branches to ignore 5 | dependencies: 6 | pre: 7 | - bower install 8 | cache_directories: 9 | - "bower_components" 10 | test: 11 | post: 12 | - grunt coveralls || true 13 | -------------------------------------------------------------------------------- /dist/js-data-angular.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * js-data-angular 3 | * @version 3.2.4 - Homepage 4 | * @copyright (c) 2014-2016 js-data-angular project authors 5 | * @license MIT 6 | * 7 | * @overview Angular wrapper for js-data. 8 | */ 9 | (function webpackUniversalModuleDefinition(root, factory) { 10 | if(typeof exports === 'object' && typeof module === 'object') 11 | module.exports = factory(require("js-data"), require("angular"), (function webpackLoadOptionalExternalModule() { try { return require("axios"); } catch(e) {} }())); 12 | else if(typeof define === 'function' && define.amd) 13 | define(["js-data", "angular"], function webpackLoadOptionalExternalModuleAmd(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_4__) { 14 | return factory(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_4__, root["axios"]); 15 | }); 16 | else if(typeof exports === 'object') 17 | exports["jsDataAngularModuleName"] = factory(require("js-data"), require("angular"), (function webpackLoadOptionalExternalModule() { try { return require("axios"); } catch(e) {} }())); 18 | else 19 | root["jsDataAngularModuleName"] = factory(root["JSData"], root["angular"], root["axios"]); 20 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_3__) { 21 | return /******/ (function(modules) { // webpackBootstrap 22 | /******/ // The module cache 23 | /******/ var installedModules = {}; 24 | /******/ 25 | /******/ // The require function 26 | /******/ function __webpack_require__(moduleId) { 27 | /******/ 28 | /******/ // Check if module is in cache 29 | /******/ if(installedModules[moduleId]) 30 | /******/ return installedModules[moduleId].exports; 31 | /******/ 32 | /******/ // Create a new module (and put it into the cache) 33 | /******/ var module = installedModules[moduleId] = { 34 | /******/ exports: {}, 35 | /******/ id: moduleId, 36 | /******/ loaded: false 37 | /******/ }; 38 | /******/ 39 | /******/ // Execute the module function 40 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 41 | /******/ 42 | /******/ // Flag the module as loaded 43 | /******/ module.loaded = true; 44 | /******/ 45 | /******/ // Return the exports of the module 46 | /******/ return module.exports; 47 | /******/ } 48 | /******/ 49 | /******/ 50 | /******/ // expose the modules object (__webpack_modules__) 51 | /******/ __webpack_require__.m = modules; 52 | /******/ 53 | /******/ // expose the module cache 54 | /******/ __webpack_require__.c = installedModules; 55 | /******/ 56 | /******/ // __webpack_public_path__ 57 | /******/ __webpack_require__.p = ""; 58 | /******/ 59 | /******/ // Load entry module and return exports 60 | /******/ return __webpack_require__(0); 61 | /******/ }) 62 | /************************************************************************/ 63 | /******/ ([ 64 | /* 0 */ 65 | /***/ function(module, exports, __webpack_require__) { 66 | 67 | 'use strict'; 68 | 69 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 70 | 71 | /* jshint loopfunc:true */ 72 | var JSData = __webpack_require__(1); 73 | var DSHttpAdapter = __webpack_require__(2); 74 | var angular = __webpack_require__(4); 75 | 76 | var DSUtils = JSData.DSUtils, 77 | DSErrors = JSData.DSErrors; 78 | var get = DSUtils.get, 79 | isString = DSUtils.isString, 80 | isNumber = DSUtils.isNumber, 81 | isObject = DSUtils.isObject, 82 | set = DSUtils.set, 83 | resolveId = DSUtils.resolveId; 84 | 85 | 86 | var adapters = [{ 87 | project: 'js-data-localstorage', 88 | name: 'localstorage', 89 | 'class': 'DSLocalStorageAdapter' 90 | }, { 91 | project: 'js-data-localforage', 92 | name: 'localforage', 93 | 'class': 'DSLocalForageAdapter' 94 | }, { 95 | project: 'js-data-firebase', 96 | name: 'firebase', 97 | 'class': 'DSFirebaseAdapter' 98 | }, { 99 | project: 'js-data-sql', 100 | name: 'sql', 101 | 'class': 'DSSqlAdapter' 102 | }]; 103 | 104 | var functionsToWrap = ['compute', 'digest', 'eject', 'inject']; 105 | 106 | function registerAdapter(adapter) { 107 | var Adapter = void 0; 108 | 109 | try { 110 | Adapter = __webpack_require__(5)(adapter.project); 111 | } catch (e) {} 112 | 113 | if (!Adapter) { 114 | Adapter = window[adapter.class]; 115 | } 116 | 117 | if (Adapter) { 118 | adapter.loaded = true; 119 | angular.module('js-data').provider(adapter.class, function () { 120 | var _this = this; 121 | _this.defaults = {}; 122 | _this.$get = [function () { 123 | return new Adapter(_this.defaults); 124 | }]; 125 | }); 126 | } 127 | } 128 | 129 | var DSHttpAdapterProvider = function DSHttpAdapterProvider() { 130 | _classCallCheck(this, DSHttpAdapterProvider); 131 | 132 | var defaults = {}; 133 | this.defaults = defaults; 134 | 135 | this.$get = ['$http', 'DS', function ($http, DS) { 136 | defaults.http = defaults.http || $http; 137 | var adapter = new DSHttpAdapter(defaults); 138 | DS.registerAdapter('http', adapter, { 'default': true }); 139 | return adapter; 140 | }]; 141 | }; 142 | 143 | var DSProvider = function DSProvider() { 144 | _classCallCheck(this, DSProvider); 145 | 146 | var _this = this; 147 | var deps = []; 148 | 149 | for (var i = 0; i < adapters.length; i++) { 150 | if (adapters[i].loaded) { 151 | deps.push(adapters[i].class); 152 | } 153 | } 154 | 155 | _this.defaults = {}; 156 | 157 | JSData.DS.prototype.bindAll = function (resourceName, params, scope, expr, cb) { 158 | var _this = this; 159 | 160 | params = params || {}; 161 | 162 | if (!_this.definitions[resourceName]) { 163 | throw new DSErrors.NER(resourceName); 164 | } else if (!isObject(params)) { 165 | throw new DSErrors.IA('"params" must be an object!'); 166 | } else if (!isObject(scope)) { 167 | throw new DSErrors.IA('"scope" must be an object!'); 168 | } else if (!isString(expr)) { 169 | throw new DSErrors.IA('"expr" must be a string!'); 170 | } 171 | 172 | var idAttribute = _this.definitions[resourceName].idAttribute; 173 | 174 | try { 175 | return scope.$watch(function () { 176 | return _this.lastModified(resourceName); 177 | }, function () { 178 | var items = _this.filter(resourceName, params); 179 | if (items && items.length) { 180 | angular.forEach(items, function (item) { 181 | _this.compute(resourceName, get(item, idAttribute)); 182 | }); 183 | } 184 | set(scope, expr, items); 185 | if (cb) { 186 | cb(null, items); 187 | } 188 | }); 189 | } catch (err) { 190 | if (cb) { 191 | cb(err); 192 | } else { 193 | throw err; 194 | } 195 | } 196 | }; 197 | 198 | JSData.DS.prototype.bindOne = function (resourceName, id, scope, expr, cb) { 199 | var _this = this; 200 | 201 | id = resolveId(_this.definitions[resourceName], id); 202 | if (!_this.definitions[resourceName]) { 203 | throw new DSErrors.NER(resourceName); 204 | } else if (!isString(id) && !isNumber(id)) { 205 | throw new DSErrors.IA('"id" must be a string or a number!'); 206 | } else if (!isObject(scope)) { 207 | throw new DSErrors.IA('"scope" must be an object!'); 208 | } else if (!isString(expr)) { 209 | throw new DSErrors.IA('"expr" must be a string!'); 210 | } 211 | 212 | try { 213 | return scope.$watch(function () { 214 | return _this.lastModified(resourceName, id); 215 | }, function () { 216 | var item = _this.get(resourceName, id); 217 | if (item) { 218 | _this.compute(resourceName, id); 219 | } 220 | set(scope, expr, item); 221 | if (cb) { 222 | cb(null, item); 223 | } 224 | }); 225 | } catch (err) { 226 | if (cb) { 227 | cb(err); 228 | } else { 229 | throw err; 230 | } 231 | } 232 | }; 233 | 234 | function load() { 235 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 236 | args[_key] = arguments[_key]; 237 | } 238 | 239 | var $rootScope = args[args.length - 2]; 240 | var $q = args[args.length - 1]; 241 | var store = new JSData.DS(_this.defaults); 242 | var originals = {}; 243 | 244 | function QPromise(executor) { 245 | var deferred = $q.defer(); 246 | 247 | try { 248 | executor(angular.bind(deferred, deferred.resolve), angular.bind(deferred, deferred.reject)); 249 | } catch (err) { 250 | deferred.reject(err); 251 | } 252 | 253 | return deferred.promise; 254 | } 255 | 256 | QPromise.all = $q.all; 257 | QPromise.when = $q.when; 258 | QPromise.reject = $q.reject; 259 | 260 | DSUtils.Promise = QPromise; 261 | 262 | // Register any adapters that have been loaded 263 | if (args.length) { 264 | for (var i = 0; i < args.length; i++) { 265 | for (var j = 0; j < adapters.length; j++) { 266 | if (adapters[j].loaded && !adapters[j].registered) { 267 | adapters[j].registered = true; 268 | store.registerAdapter(adapters[j].name, args[i]); 269 | break; 270 | } 271 | } 272 | } 273 | } 274 | 275 | // Wrap certain sync functions with $apply 276 | 277 | var _loop = function _loop() { 278 | var name = functionsToWrap[k]; 279 | originals[name] = store[name]; 280 | store[name] = function () { 281 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 282 | args[_key2] = arguments[_key2]; 283 | } 284 | 285 | if (!$rootScope.$$phase) { 286 | return $rootScope.$apply(function () { 287 | return originals[name].apply(store, args); 288 | }); 289 | } 290 | return originals[name].apply(store, args); 291 | }; 292 | }; 293 | 294 | for (var k = 0; k < functionsToWrap.length; k++) { 295 | _loop(); 296 | } 297 | 298 | // Hook into the digest loop 299 | if (typeof Object.observe !== 'function' || typeof Array.observe !== 'function') { 300 | $rootScope.$watch(function () { 301 | return store.observe.Platform.performMicrotaskCheckpoint(); 302 | }); 303 | } 304 | 305 | return store; 306 | } 307 | 308 | deps.push('$rootScope'); 309 | deps.push('$q'); 310 | deps.push(load); 311 | 312 | _this.$get = deps; 313 | }; 314 | 315 | angular.module('js-data', ['ng']).value('DSUtils', DSUtils).value('DSErrors', DSErrors).value('DSVersion', JSData.version).provider('DS', DSProvider).provider('DSHttpAdapter', DSHttpAdapterProvider).run(['DS', 'DSHttpAdapter', function (DS, DSHttpAdapter) { 316 | DS.registerAdapter('http', DSHttpAdapter, { 'default': true }); 317 | }]); 318 | 319 | for (var i = 0; i < adapters.length; i++) { 320 | registerAdapter(adapters[i]); 321 | } 322 | 323 | // return the module name 324 | module.exports = 'js-data'; 325 | try { 326 | module.exports.name = 'js-data'; 327 | } catch (e) {} 328 | 329 | /***/ }, 330 | /* 1 */ 331 | /***/ function(module, exports) { 332 | 333 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 334 | 335 | /***/ }, 336 | /* 2 */ 337 | /***/ function(module, exports, __webpack_require__) { 338 | 339 | 'use strict'; 340 | 341 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 342 | 343 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 344 | 345 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 346 | 347 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 348 | 349 | var JSData = __webpack_require__(1); 350 | var axios = null; 351 | 352 | try { 353 | axios = __webpack_require__(3); 354 | } catch (e) {} 355 | 356 | var DSUtils = JSData.DSUtils; 357 | var deepMixIn = DSUtils.deepMixIn, 358 | removeCircular = DSUtils.removeCircular, 359 | copy = DSUtils.copy, 360 | makePath = DSUtils.makePath, 361 | isString = DSUtils.isString, 362 | isNumber = DSUtils.isNumber; 363 | 364 | 365 | function isUndefined(value) { 366 | return value === undefined; 367 | } 368 | 369 | var Defaults = function () { 370 | function Defaults() { 371 | _classCallCheck(this, Defaults); 372 | } 373 | 374 | _createClass(Defaults, [{ 375 | key: 'queryTransform', 376 | value: function queryTransform(resourceConfig, params) { 377 | return params; 378 | } 379 | }, { 380 | key: 'deserialize', 381 | value: function deserialize(resourceConfig, data) { 382 | return data ? 'data' in data ? data.data : data : data; 383 | } 384 | }, { 385 | key: 'serialize', 386 | value: function serialize(resourceConfig, data) { 387 | return data; 388 | } 389 | }, { 390 | key: 'log', 391 | value: function log() {} 392 | }, { 393 | key: 'error', 394 | value: function error() {} 395 | }]); 396 | 397 | return Defaults; 398 | }(); 399 | 400 | var defaultsPrototype = Defaults.prototype; 401 | 402 | defaultsPrototype.basePath = ''; 403 | 404 | defaultsPrototype.forceTrailingSlash = ''; 405 | 406 | defaultsPrototype.httpConfig = {}; 407 | 408 | defaultsPrototype.verbsUseBasePath = false; 409 | 410 | var DSHttpAdapter = function () { 411 | function DSHttpAdapter(options) { 412 | _classCallCheck(this, DSHttpAdapter); 413 | 414 | options || (options = {}); 415 | this.defaults = new Defaults(); 416 | if (console) { 417 | this.defaults.log = function (a, b) { 418 | return console[typeof console.info === 'function' ? 'info' : 'log'](a, b); 419 | }; 420 | } 421 | if (console) { 422 | this.defaults.error = function (a, b) { 423 | return console[typeof console.error === 'function' ? 'error' : 'log'](a, b); 424 | }; 425 | } 426 | deepMixIn(this.defaults, options); 427 | this.http = options.http || axios; 428 | } 429 | 430 | _createClass(DSHttpAdapter, [{ 431 | key: 'getEndpoint', 432 | value: function getEndpoint(resourceConfig, id, options) { 433 | options || (options = {}); 434 | options.params = isUndefined(options.params) ? {} : options.params; 435 | 436 | var endpoint = options.hasOwnProperty('endpoint') ? options.endpoint : resourceConfig.endpoint; 437 | var parents = resourceConfig.parents || (resourceConfig.parent ? _defineProperty({}, resourceConfig.parent, { 438 | key: resourceConfig.parentKey, 439 | field: resourceConfig.parentField 440 | }) : {}); 441 | 442 | DSUtils.forOwn(parents, function (parent, parentName) { 443 | var item = void 0; 444 | var parentKey = parent.key; 445 | var parentField = parent.field; 446 | var parentDef = resourceConfig.getResource(parentName); 447 | var parentId = options.params[parentKey]; 448 | 449 | if (parentId === false || !parentKey || !parentDef) { 450 | if (parentId === false) { 451 | delete options.params[parentKey]; 452 | } 453 | } else { 454 | delete options.params[parentKey]; 455 | 456 | if (DSUtils._sn(id)) { 457 | item = resourceConfig.get(id); 458 | } else if (DSUtils._o(id)) { 459 | item = id; 460 | } 461 | 462 | if (item) { 463 | parentId = parentId || item[parentKey] || (item[parentField] ? item[parentField][parentDef.idAttribute] : null); 464 | } 465 | 466 | if (parentId) { 467 | delete options.endpoint; 468 | var _options = {}; 469 | DSUtils.forOwn(options, function (value, key) { 470 | _options[key] = value; 471 | }); 472 | endpoint = DSUtils.makePath(this.getEndpoint(parentDef, parentId, DSUtils._(parentDef, _options)), parentId, endpoint); 473 | } 474 | } 475 | }, this); 476 | 477 | return endpoint; 478 | } 479 | }, { 480 | key: 'getPath', 481 | value: function getPath(method, resourceConfig, id, options) { 482 | var _this = this; 483 | options || (options = {}); 484 | if (isString(options.urlPath)) { 485 | return makePath.apply(DSUtils, [options.basePath || resourceConfig.basePath || _this.defaults.basePath, options.urlPath]); 486 | } else { 487 | var args = [options.basePath || resourceConfig.basePath || _this.defaults.basePath, this.getEndpoint(resourceConfig, isString(id) || isNumber(id) || method === 'create' ? id : null, options)]; 488 | if (method === 'find' || method === 'update' || method === 'destroy') { 489 | args.push(id); 490 | } 491 | return makePath.apply(DSUtils, args); 492 | } 493 | } 494 | }, { 495 | key: 'HTTP', 496 | value: function HTTP(config) { 497 | var _this = this; 498 | var start = new Date(); 499 | 500 | // blacklist `data` as it can be large and will take a lot of time to copy 501 | var payload = config.data; 502 | var cache = config.cache; 503 | var timeout = config.timeout; 504 | var params = config.params; 505 | config = copy(config, null, null, null, ['data', 'cache', 'timeout', 'params']); // params could have data, cache, timeout 506 | config.params = copy(params); 507 | config = deepMixIn(config, _this.defaults.httpConfig); 508 | config.data = payload; 509 | config.cache = cache; 510 | config.timeout = timeout; 511 | if (!('verbsUseBasePath' in config)) { 512 | config.verbsUseBasePath = _this.defaults.verbsUseBasePath; 513 | } 514 | if (!config.urlOverride && config.verbsUseBasePath) { 515 | config.url = makePath(config.basePath || _this.defaults.basePath, config.url); 516 | } 517 | if (_this.defaults.forceTrailingSlash && config.url[config.url.length - 1] !== '/' && !config.urlOverride) { 518 | config.url += '/'; 519 | } 520 | if (_typeof(config.data) === 'object') { 521 | config.data = removeCircular(config.data); 522 | } 523 | config.method = config.method.toUpperCase(); 524 | var suffix = isUndefined(config.suffix) ? _this.defaults.suffix : config.suffix; 525 | if (suffix && config.url.substr(config.url.length - suffix.length) !== suffix && !config.urlOverride) { 526 | config.url += suffix; 527 | } 528 | 529 | // logs the HTTP response 530 | function logResponse(data, isRejection) { 531 | data = data || {}; 532 | // examine the data object 533 | if (data instanceof Error) { 534 | // log the Error object 535 | _this.defaults.error('FAILED: ' + (data.message || 'Unknown Error'), data); 536 | return DSUtils.Promise.reject(data); 537 | } else if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === 'object') { 538 | var str = start.toUTCString() + ' - ' + config.method + ' ' + config.url + ' - ' + data.status + ' ' + (new Date().getTime() - start.getTime()) + 'ms'; 539 | 540 | if (data.status >= 200 && data.status < 300 && !isRejection) { 541 | if (_this.defaults.log) { 542 | _this.defaults.log(str, data); 543 | } 544 | return data; 545 | } else { 546 | if (_this.defaults.error) { 547 | _this.defaults.error('FAILED: ' + str, data); 548 | } 549 | return DSUtils.Promise.reject(data); 550 | } 551 | } else { 552 | // unknown type for 'data' that is not an Object or Error 553 | _this.defaults.error('FAILED', data); 554 | return DSUtils.Promise.reject(data); 555 | } 556 | } 557 | 558 | if (!this.http) { 559 | throw new Error('You have not configured this adapter with an http library!'); 560 | } 561 | 562 | return this.http(config).then(logResponse, function (data) { 563 | return logResponse(data, true); 564 | }); 565 | } 566 | }, { 567 | key: 'GET', 568 | value: function GET(url, config) { 569 | config = config || {}; 570 | config.method = config.method || 'get'; 571 | config.urlOverride = !!config.url; 572 | config.url = config.url || url; 573 | return this.HTTP(config); 574 | } 575 | }, { 576 | key: 'POST', 577 | value: function POST(url, attrs, config) { 578 | config = config || {}; 579 | config.method = config.method || 'post'; 580 | config.urlOverride = !!config.url; 581 | config.url = config.url || url; 582 | config.data = config.data || attrs; 583 | return this.HTTP(config); 584 | } 585 | }, { 586 | key: 'PUT', 587 | value: function PUT(url, attrs, config) { 588 | config = config || {}; 589 | config.method = config.method || 'put'; 590 | config.urlOverride = !!config.url; 591 | config.url = config.url || url; 592 | config.data = config.data || attrs; 593 | return this.HTTP(config); 594 | } 595 | }, { 596 | key: 'DEL', 597 | value: function DEL(url, config) { 598 | config = config || {}; 599 | config.method = config.method || 'delete'; 600 | config.urlOverride = !!config.url; 601 | config.url = config.url || url; 602 | return this.HTTP(config); 603 | } 604 | }, { 605 | key: 'find', 606 | value: function find(resourceConfig, id, options) { 607 | var _this = this; 608 | options || (options = {}); 609 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 610 | options.params = isUndefined(options.params) ? {} : copy(options.params); 611 | options.params = _this.defaults.queryTransform(resourceConfig, options.params); 612 | return _this.GET(_this.getPath('find', resourceConfig, id, options), options).then(function (data) { 613 | var item = (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 614 | return !item ? DSUtils.Promise.reject(new Error('Not Found!')) : item; 615 | }); 616 | } 617 | }, { 618 | key: 'findAll', 619 | value: function findAll(resourceConfig, params, options) { 620 | var _this = this; 621 | options || (options = {}); 622 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 623 | options.params = isUndefined(options.params) ? {} : copy(options.params); 624 | if (params) { 625 | params = _this.defaults.queryTransform(resourceConfig, params); 626 | deepMixIn(options.params, params); 627 | } 628 | return _this.GET(_this.getPath('findAll', resourceConfig, params, options), options).then(function (data) { 629 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 630 | }); 631 | } 632 | }, { 633 | key: 'create', 634 | value: function create(resourceConfig, attrs, options) { 635 | var _this = this; 636 | options || (options = {}); 637 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 638 | options.params = isUndefined(options.params) ? {} : copy(options.params); 639 | options.params = _this.defaults.queryTransform(resourceConfig, options.params); 640 | return _this.POST(_this.getPath('create', resourceConfig, attrs, options), options.serialize ? options.serialize(resourceConfig, attrs) : _this.defaults.serialize(resourceConfig, attrs), options).then(function (data) { 641 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 642 | }); 643 | } 644 | }, { 645 | key: 'update', 646 | value: function update(resourceConfig, id, attrs, options) { 647 | var _this = this; 648 | options || (options = {}); 649 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 650 | options.params = isUndefined(options.params) ? {} : copy(options.params); 651 | options.params = _this.defaults.queryTransform(resourceConfig, options.params); 652 | return _this.PUT(_this.getPath('update', resourceConfig, id, options), options.serialize ? options.serialize(resourceConfig, attrs) : _this.defaults.serialize(resourceConfig, attrs), options).then(function (data) { 653 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 654 | }); 655 | } 656 | }, { 657 | key: 'updateAll', 658 | value: function updateAll(resourceConfig, attrs, params, options) { 659 | var _this = this; 660 | options || (options = {}); 661 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 662 | options.params = isUndefined(options.params) ? {} : copy(options.params); 663 | if (params) { 664 | params = _this.defaults.queryTransform(resourceConfig, params); 665 | deepMixIn(options.params, params); 666 | } 667 | return this.PUT(_this.getPath('updateAll', resourceConfig, attrs, options), options.serialize ? options.serialize(resourceConfig, attrs) : _this.defaults.serialize(resourceConfig, attrs), options).then(function (data) { 668 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 669 | }); 670 | } 671 | }, { 672 | key: 'destroy', 673 | value: function destroy(resourceConfig, id, options) { 674 | var _this = this; 675 | options || (options = {}); 676 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 677 | options.params = isUndefined(options.params) ? {} : copy(options.params); 678 | options.params = _this.defaults.queryTransform(resourceConfig, options.params); 679 | return _this.DEL(_this.getPath('destroy', resourceConfig, id, options), options).then(function (data) { 680 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 681 | }); 682 | } 683 | }, { 684 | key: 'destroyAll', 685 | value: function destroyAll(resourceConfig, params, options) { 686 | var _this = this; 687 | options || (options = {}); 688 | options.suffix = isUndefined(options.suffix) ? resourceConfig.suffix : options.suffix; 689 | options.params = isUndefined(options.params) ? {} : copy(options.params); 690 | if (params) { 691 | params = _this.defaults.queryTransform(resourceConfig, params); 692 | deepMixIn(options.params, params); 693 | } 694 | return this.DEL(_this.getPath('destroyAll', resourceConfig, params, options), options).then(function (data) { 695 | return (options.deserialize ? options.deserialize : _this.defaults.deserialize)(resourceConfig, data); 696 | }); 697 | } 698 | }]); 699 | 700 | return DSHttpAdapter; 701 | }(); 702 | 703 | DSHttpAdapter.version = { 704 | full: '3.2.4', 705 | major: parseInt('3', 10), 706 | minor: parseInt('2', 10), 707 | patch: parseInt('4', 10), 708 | alpha: true ? 'false' : false, 709 | beta: true ? 'false' : false 710 | }; 711 | 712 | module.exports = DSHttpAdapter; 713 | 714 | /***/ }, 715 | /* 3 */ 716 | /***/ function(module, exports) { 717 | 718 | if(typeof __WEBPACK_EXTERNAL_MODULE_3__ === 'undefined') {var e = new Error("Cannot find module \"axios\""); e.code = 'MODULE_NOT_FOUND'; throw e;} 719 | module.exports = __WEBPACK_EXTERNAL_MODULE_3__; 720 | 721 | /***/ }, 722 | /* 4 */ 723 | /***/ function(module, exports) { 724 | 725 | module.exports = __WEBPACK_EXTERNAL_MODULE_4__; 726 | 727 | /***/ }, 728 | /* 5 */ 729 | /***/ function(module, exports, __webpack_require__) { 730 | 731 | var map = {}; 732 | function webpackContext(req) { 733 | return __webpack_require__(webpackContextResolve(req)); 734 | }; 735 | function webpackContextResolve(req) { 736 | return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }()); 737 | }; 738 | webpackContext.keys = function webpackContextKeys() { 739 | return Object.keys(map); 740 | }; 741 | webpackContext.resolve = webpackContextResolve; 742 | module.exports = webpackContext; 743 | webpackContext.id = 5; 744 | 745 | 746 | /***/ } 747 | /******/ ]) 748 | }); 749 | ; 750 | //# sourceMappingURL=js-data-angular.js.map -------------------------------------------------------------------------------- /dist/js-data-angular.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * js-data-angular 3 | * @version 3.2.4 - Homepage 4 | * @copyright (c) 2014-2016 js-data-angular project authors 5 | * @license MIT 6 | * 7 | * @overview Angular wrapper for js-data. 8 | */ 9 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("js-data"),require("angular"),function(){try{return require("axios")}catch(e){}}()):"function"==typeof define&&define.amd?define(["js-data","angular"],function(r,a){return t(r,a,e.axios)}):"object"==typeof exports?exports.jsDataAngularModuleName=t(require("js-data"),require("angular"),function(){try{return require("axios")}catch(e){}}()):e.jsDataAngularModuleName=t(e.JSData,e.angular,e.axios)}(this,function(e,t,r){return function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e){var t=void 0;try{t=r(5)(e.project)}catch(e){}t||(t=window[e.class]),t&&(e.loaded=!0,u.module("js-data").provider(e.class,function(){var e=this;e.defaults={},e.$get=[function(){return new t(e.defaults)}]}))}var i=r(1),s=r(2),u=r(4),o=i.DSUtils,f=i.DSErrors,l=o.get,d=o.isString,c=o.isNumber,p=o.isObject,h=o.set,m=o.resolveId,v=[{project:"js-data-localstorage",name:"localstorage",class:"DSLocalStorageAdapter"},{project:"js-data-localforage",name:"localforage",class:"DSLocalForageAdapter"},{project:"js-data-firebase",name:"firebase",class:"DSFirebaseAdapter"},{project:"js-data-sql",name:"sql",class:"DSSqlAdapter"}],y=["compute","digest","eject","inject"],g=function e(){a(this,e);var t={};this.defaults=t,this.$get=["$http","DS",function(e,r){t.http=t.http||e;var a=new s(t);return r.registerAdapter("http",a,{default:!0}),a}]},b=function e(){function t(){function e(e){var t=f.defer();try{e(u.bind(t,t.resolve),u.bind(t,t.reject))}catch(e){t.reject(e)}return t.promise}for(var t=arguments.length,a=Array(t),n=0;t>n;n++)a[n]=arguments[n];var s=a[a.length-2],f=a[a.length-1],l=new i.DS(r.defaults),d={};if(e.all=f.all,e.when=f.when,e.reject=f.reject,o.Promise=e,a.length)for(var c=0;ca;a++)r[a]=arguments[a];return s.$$phase?d[e].apply(l,r):s.$apply(function(){return d[e].apply(l,r)})}},m=0;m=200&&t.status<300&&!n?(r.defaults.log&&r.defaults.log(i,t),t):(r.defaults.error&&r.defaults.error("FAILED: "+i,t),l.Promise.reject(t))}return r.defaults.error("FAILED",t),l.Promise.reject(t)}var r=this,a=new Date,n=e.data,u=e.cache,o=e.timeout,f=e.params;e=p(e,null,null,null,["data","cache","timeout","params"]),e.params=p(f),e=d(e,r.defaults.httpConfig),e.data=n,e.cache=u,e.timeout=o,"verbsUseBasePath"in e||(e.verbsUseBasePath=r.defaults.verbsUseBasePath),!e.urlOverride&&e.verbsUseBasePath&&(e.url=h(e.basePath||r.defaults.basePath,e.url)),r.defaults.forceTrailingSlash&&"/"!==e.url[e.url.length-1]&&!e.urlOverride&&(e.url+="/"),"object"===s(e.data)&&(e.data=c(e.data)),e.method=e.method.toUpperCase();var m=i(e.suffix)?r.defaults.suffix:e.suffix;if(m&&e.url.substr(e.url.length-m.length)!==m&&!e.urlOverride&&(e.url+=m),!this.http)throw new Error("You have not configured this adapter with an http library!");return this.http(e).then(t,function(e){return t(e,!0)})}},{key:"GET",value:function(e,t){return t=t||{},t.method=t.method||"get",t.urlOverride=!!t.url,t.url=t.url||e,this.HTTP(t)}},{key:"POST",value:function(e,t,r){return r=r||{},r.method=r.method||"post",r.urlOverride=!!r.url,r.url=r.url||e,r.data=r.data||t,this.HTTP(r)}},{key:"PUT",value:function(e,t,r){return r=r||{},r.method=r.method||"put",r.urlOverride=!!r.url,r.url=r.url||e,r.data=r.data||t,this.HTTP(r)}},{key:"DEL",value:function(e,t){return t=t||{},t.method=t.method||"delete",t.urlOverride=!!t.url,t.url=t.url||e,this.HTTP(t)}},{key:"find",value:function(e,t,r){var a=this;return r||(r={}),r.suffix=i(r.suffix)?e.suffix:r.suffix,r.params=i(r.params)?{}:p(r.params),r.params=a.defaults.queryTransform(e,r.params),a.GET(a.getPath("find",e,t,r),r).then(function(t){var n=(r.deserialize?r.deserialize:a.defaults.deserialize)(e,t);return n?n:l.Promise.reject(new Error("Not Found!"))})}},{key:"findAll",value:function(e,t,r){var a=this;return r||(r={}),r.suffix=i(r.suffix)?e.suffix:r.suffix,r.params=i(r.params)?{}:p(r.params),t&&(t=a.defaults.queryTransform(e,t),d(r.params,t)),a.GET(a.getPath("findAll",e,t,r),r).then(function(t){return(r.deserialize?r.deserialize:a.defaults.deserialize)(e,t)})}},{key:"create",value:function(e,t,r){var a=this;return r||(r={}),r.suffix=i(r.suffix)?e.suffix:r.suffix,r.params=i(r.params)?{}:p(r.params),r.params=a.defaults.queryTransform(e,r.params),a.POST(a.getPath("create",e,t,r),r.serialize?r.serialize(e,t):a.defaults.serialize(e,t),r).then(function(t){return(r.deserialize?r.deserialize:a.defaults.deserialize)(e,t)})}},{key:"update",value:function(e,t,r,a){var n=this;return a||(a={}),a.suffix=i(a.suffix)?e.suffix:a.suffix,a.params=i(a.params)?{}:p(a.params),a.params=n.defaults.queryTransform(e,a.params),n.PUT(n.getPath("update",e,t,a),a.serialize?a.serialize(e,r):n.defaults.serialize(e,r),a).then(function(t){return(a.deserialize?a.deserialize:n.defaults.deserialize)(e,t)})}},{key:"updateAll",value:function(e,t,r,a){var n=this;return a||(a={}),a.suffix=i(a.suffix)?e.suffix:a.suffix,a.params=i(a.params)?{}:p(a.params),r&&(r=n.defaults.queryTransform(e,r),d(a.params,r)),this.PUT(n.getPath("updateAll",e,t,a),a.serialize?a.serialize(e,t):n.defaults.serialize(e,t),a).then(function(t){return(a.deserialize?a.deserialize:n.defaults.deserialize)(e,t)})}},{key:"destroy",value:function(e,t,r){var a=this;return r||(r={}),r.suffix=i(r.suffix)?e.suffix:r.suffix,r.params=i(r.params)?{}:p(r.params),r.params=a.defaults.queryTransform(e,r.params),a.DEL(a.getPath("destroy",e,t,r),r).then(function(t){return(r.deserialize?r.deserialize:a.defaults.deserialize)(e,t)})}},{key:"destroyAll",value:function(e,t,r){var a=this;return r||(r={}),r.suffix=i(r.suffix)?e.suffix:r.suffix,r.params=i(r.params)?{}:p(r.params),t&&(t=a.defaults.queryTransform(e,t),d(r.params,t)),this.DEL(a.getPath("destroyAll",e,t,r),r).then(function(t){return(r.deserialize?r.deserialize:a.defaults.deserialize)(e,t)})}}]),e}();b.version={full:"<%= pkg.version %>",major:parseInt("<%= major %>",10),minor:parseInt("<%= minor %>",10),patch:parseInt("<%= patch %>",10),alpha:"<%= alpha %>",beta:"<%= beta %>"},e.exports=b},function(e,t){if("undefined"==typeof r){var a=new Error('Cannot find module "axios"');throw a.code="MODULE_NOT_FOUND",a}e.exports=r},function(e,r){e.exports=t},function(e,t,r){function a(e){return r(n(e))}function n(e){return i[e]||function(){throw new Error("Cannot find module '"+e+"'.")}()}var i={};a.keys=function(){return Object.keys(i)},a.resolve=n,e.exports=a,a.id=5}])}); 10 | //# sourceMappingURL=js-data-angular.min.map -------------------------------------------------------------------------------- /dist/js-data-angular.min.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["dist/js-data-angular.js"],"names":["root","factory","exports","module","require","e","define","amd","__WEBPACK_EXTERNAL_MODULE_1__","__WEBPACK_EXTERNAL_MODULE_4__","this","__WEBPACK_EXTERNAL_MODULE_3__","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","_classCallCheck","instance","Constructor","TypeError","registerAdapter","adapter","Adapter","project","window","class","angular","provider","_this","defaults","$get","JSData","DSHttpAdapter","DSUtils","DSErrors","get","isString","isNumber","isObject","set","resolveId","adapters","name","functionsToWrap","DSHttpAdapterProvider","$http","DS","http","default","DSProvider","load","QPromise","executor","deferred","$q","defer","bind","resolve","reject","err","promise","_len","arguments","length","args","Array","_key","$rootScope","store","originals","all","when","Promise","i","j","registered","_loop","k","_len2","_key2","$$phase","apply","$apply","Object","observe","$watch","Platform","performMicrotaskCheckpoint","deps","push","prototype","bindAll","resourceName","params","scope","expr","cb","definitions","NER","IA","idAttribute","lastModified","items","filter","forEach","item","compute","bindOne","value","version","run","_defineProperty","obj","key","defineProperty","enumerable","configurable","writable","isUndefined","undefined","_typeof","Symbol","iterator","constructor","_createClass","defineProperties","target","props","descriptor","protoProps","staticProps","axios","deepMixIn","removeCircular","copy","makePath","Defaults","resourceConfig","data","defaultsPrototype","basePath","forceTrailingSlash","httpConfig","verbsUseBasePath","options","console","log","a","b","info","error","endpoint","hasOwnProperty","parents","parent","parentKey","field","parentField","forOwn","parentName","parentDef","getResource","parentId","_sn","_o","_options","getEndpoint","_","method","urlPath","config","logResponse","isRejection","Error","message","str","start","toUTCString","url","status","Date","getTime","payload","cache","timeout","urlOverride","toUpperCase","suffix","substr","then","HTTP","attrs","queryTransform","GET","getPath","deserialize","POST","serialize","PUT","DEL","full","major","parseInt","minor","patch","alpha","beta","code","webpackContext","req","webpackContextResolve","map","keys"],"mappings":"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","file":"dist/js-data-angular.min.js"} -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var customLaunchers = { 2 | bs_ie9_windows7: { 3 | base: 'BrowserStack', 4 | browser: 'ie', 5 | browser_version: '9.0', 6 | os: 'Windows', 7 | os_version: '7' 8 | }, 9 | bs_safari7_osxmavericks: { 10 | base: 'BrowserStack', 11 | browser: 'safari', 12 | browser_version: '7.1', 13 | os: 'OS X', 14 | os_version: 'Mavericks' 15 | }, 16 | bs_firefox41_windows7: { 17 | base: 'BrowserStack', 18 | browser: 'firefox', 19 | browser_version: '41.0', 20 | os: 'Windows', 21 | os_version: '7' 22 | }, 23 | bs_chrome46_windows7: { 24 | base: 'BrowserStack', 25 | browser: 'chrome', 26 | browser_version: '46.0', 27 | os: 'Windows', 28 | os_version: '7' 29 | } 30 | } 31 | 32 | var browsers = ['PhantomJS'] 33 | if ( 34 | process.env.BROWSERSTACK_USERNAME && 35 | process.env.BROWSERSTACK_ACCESS_KEY 36 | ) { 37 | browsers = browsers.concat(Object.keys(customLaunchers)) 38 | } 39 | 40 | module.exports = function (config) { 41 | config.set({ 42 | basePath: './', 43 | frameworks: ['sinon', 'chai', 'mocha'], 44 | plugins: [ 45 | 'karma-sinon', 46 | 'karma-mocha', 47 | 'karma-chai', 48 | 'karma-phantomjs-launcher', 49 | 'karma-coverage' 50 | ], 51 | autoWatch: false, 52 | browserNoActivityTimeout: 30000, 53 | browsers: browsers, 54 | files: [ 55 | 'bower_components/angular-1.3.2/angular.js', 56 | 'bower_components/angular-mocks-1.3.2/angular-mocks.js', 57 | 'node_modules/babel-polyfill/dist/polyfill.js', 58 | 'node_modules/js-data/dist/js-data.js', 59 | 'dist/js-data-angular.js', 60 | 'karma.start.js', 61 | 'test/**/*.js' 62 | ], 63 | reporters: ['dots', 'coverage'], 64 | preprocessors: { 65 | 'dist/js-data-angular.js': ['coverage'] 66 | }, 67 | coverageReporter: { 68 | type: 'lcov', 69 | dir: 'coverage/' 70 | }, 71 | port: 9876, 72 | runnerPort: 9100, 73 | colors: true, 74 | logLevel: config.LOG_INFO, 75 | captureTimeout: 30000, 76 | singleRun: true 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /karma.start.js: -------------------------------------------------------------------------------- 1 | // Setup global test variables 2 | var $rootScope, $q, $log, $timeout, DSHttpAdapterProvider, DSProvider, DS, DSUtils, DSHttpAdapter, app, $httpBackend, p1, p2, p3, p4, p5 3 | 4 | var Post, User, Organization, Comment, Profile 5 | var user1, organization2, comment3, profile4 6 | var comment11, comment12, comment13, organization14, profile15, user10, user16, user17, user18, organization15, user19, user20, comment19, user22, profile21 7 | 8 | var lifecycle = {} 9 | 10 | // Helper globals 11 | var fail = function (msg) { 12 | if (msg instanceof Error) { 13 | console.log(msg.stack) 14 | } else { 15 | assert.equal('should not reach this!: ' + msg, 'failure') 16 | } 17 | }, 18 | TYPES_EXCEPT_STRING = [123, 123.123, null, undefined, {}, [], true, false, function () { 19 | }], 20 | TYPES_EXCEPT_STRING_OR_ARRAY = [123, 123.123, null, undefined, {}, true, false, function () { 21 | }], 22 | TYPES_EXCEPT_STRING_OR_OBJECT = [123, 123.123, null, undefined, [], true, false, function () { 23 | }], 24 | TYPES_EXCEPT_STRING_OR_NUMBER_OBJECT = [null, undefined, [], true, false, function () { 25 | }], 26 | TYPES_EXCEPT_ARRAY = ['string', 123, 123.123, null, undefined, {}, true, false, function () { 27 | }], 28 | TYPES_EXCEPT_STRING_OR_NUMBER = [null, undefined, {}, [], true, false, function () { 29 | }], 30 | TYPES_EXCEPT_STRING_OR_ARRAY_OR_NUMBER = [null, undefined, {}, true, false, function () { 31 | }], 32 | TYPES_EXCEPT_NUMBER = ['string', null, undefined, {}, [], true, false, function () { 33 | }], 34 | TYPES_EXCEPT_OBJECT = ['string', 123, 123.123, null, undefined, true, false, function () { 35 | }], 36 | TYPES_EXCEPT_BOOLEAN = ['string', 123, 123.123, null, undefined, {}, [], function () { 37 | }], 38 | TYPES_EXCEPT_FUNCTION = ['string', 123, 123.123, null, undefined, {}, [], true, false] 39 | 40 | angular.module('app', ['ng', 'js-data']) 41 | 42 | // Setup before each test 43 | beforeEach(function () { 44 | lifecycle.beforeValidate = function (resourceName, attrs, cb) { 45 | lifecycle.beforeValidate.callCount += 1 46 | cb(null, attrs) 47 | } 48 | lifecycle.validate = function (resourceName, attrs, cb) { 49 | lifecycle.validate.callCount += 1 50 | cb(null, attrs) 51 | } 52 | lifecycle.afterValidate = function (resourceName, attrs, cb) { 53 | lifecycle.afterValidate.callCount += 1 54 | cb(null, attrs) 55 | } 56 | lifecycle.beforeCreate = function (resourceName, attrs, cb) { 57 | lifecycle.beforeCreate.callCount += 1 58 | cb(null, attrs) 59 | } 60 | lifecycle.afterCreate = function (resourceName, attrs, cb) { 61 | lifecycle.afterCreate.callCount += 1 62 | cb(null, attrs) 63 | } 64 | lifecycle.beforeUpdate = function (resourceName, attrs, cb) { 65 | lifecycle.beforeUpdate.callCount += 1 66 | cb(null, attrs) 67 | } 68 | lifecycle.afterUpdate = function (resourceName, attrs, cb) { 69 | lifecycle.afterUpdate.callCount += 1 70 | cb(null, attrs) 71 | } 72 | lifecycle.beforeDestroy = function (resourceName, attrs, cb) { 73 | lifecycle.beforeDestroy.callCount += 1 74 | cb(null, attrs) 75 | } 76 | lifecycle.afterDestroy = function (resourceName, attrs, cb) { 77 | lifecycle.afterDestroy.callCount += 1 78 | cb(null, attrs) 79 | } 80 | lifecycle.beforeInject = function () { 81 | lifecycle.beforeInject.callCount += 1 82 | } 83 | lifecycle.afterInject = function (resource, data) { 84 | lifecycle.afterInject.callCount += 1 85 | } 86 | lifecycle.serialize = function (resourceName, data) { 87 | lifecycle.serialize.callCount += 1 88 | return data 89 | } 90 | lifecycle.deserialize = function (resourceName, data) { 91 | lifecycle.deserialize.callCount += 1 92 | return data ? (data.data ? data.data : data) : data 93 | } 94 | lifecycle.queryTransform = function (resourceName, query) { 95 | lifecycle.queryTransform.callCount += 1 96 | return query 97 | } 98 | module('app', function (_DSProvider_, _DSHttpAdapterProvider_) { 99 | DSProvider = _DSProvider_ 100 | DSProvider.defaults.basePath = 'http://test.angular-cache.com' 101 | DSProvider.defaults.beforeValidate = lifecycle.beforeValidate 102 | DSProvider.defaults.validate = lifecycle.validate 103 | DSProvider.defaults.afterValidate = lifecycle.afterValidate 104 | DSProvider.defaults.beforeCreate = lifecycle.beforeCreate 105 | DSProvider.defaults.afterCreate = lifecycle.afterCreate 106 | DSProvider.defaults.beforeUpdate = lifecycle.beforeUpdate 107 | DSProvider.defaults.afterUpdate = lifecycle.afterUpdate 108 | DSProvider.defaults.beforeDestroy = lifecycle.beforeDestroy 109 | DSProvider.defaults.afterDestroy = lifecycle.afterDestroy 110 | DSProvider.defaults.beforeInject = lifecycle.beforeInject 111 | DSProvider.defaults.afterInject = lifecycle.afterInject 112 | DSProvider.defaults.serialize = lifecycle.serialize 113 | DSProvider.defaults.deserialize = lifecycle.deserialize 114 | DSHttpAdapterProvider = _DSHttpAdapterProvider_ 115 | DSHttpAdapterProvider.defaults.queryTransform = lifecycle.queryTransform 116 | DSHttpAdapterProvider.defaults.log = false 117 | }) 118 | }) 119 | 120 | function startInjector() { 121 | inject(function (_$rootScope_, _$q_, _$timeout_, _$httpBackend_, _DS_, _$log_, _DSUtils_, _DSHttpAdapter_) { 122 | // Setup global mocks 123 | 124 | localStorage.clear() 125 | $q = _$q_ 126 | $rootScope = _$rootScope_ 127 | DS = _DS_ 128 | $timeout = _$timeout_ 129 | DSUtils = _DSUtils_ 130 | DSHttpAdapter = _DSHttpAdapter_ 131 | $httpBackend = _$httpBackend_ 132 | Post = DS.defineResource({ 133 | name: 'post', 134 | keepChangeHistory: true, 135 | endpoint: '/posts' 136 | }) 137 | User = DS.defineResource({ 138 | name: 'user', 139 | relations: { 140 | hasMany: { 141 | comment: { 142 | localField: 'comments', 143 | foreignKey: 'approvedBy' 144 | } 145 | }, 146 | hasOne: { 147 | profile: { 148 | localField: 'profile', 149 | foreignKey: 'userId' 150 | } 151 | }, 152 | belongsTo: { 153 | organization: { 154 | parent: true, 155 | localKey: 'organizationId', 156 | localField: 'organization' 157 | } 158 | } 159 | } 160 | }) 161 | 162 | Organization = DS.defineResource({ 163 | name: 'organization', 164 | relations: { 165 | hasMany: { 166 | user: { 167 | localField: 'users', 168 | foreignKey: 'organizationId' 169 | } 170 | } 171 | } 172 | }) 173 | 174 | Profile = DS.defineResource({ 175 | name: 'profile', 176 | relations: { 177 | belongsTo: { 178 | user: { 179 | localField: 'user', 180 | localKey: 'userId' 181 | } 182 | } 183 | } 184 | }) 185 | 186 | Comment = DS.defineResource({ 187 | name: 'comment', 188 | relations: { 189 | belongsTo: { 190 | user: [ 191 | { 192 | localField: 'user', 193 | localKey: 'userId' 194 | }, 195 | { 196 | parent: true, 197 | localField: 'approvedByUser', 198 | localKey: 'approvedBy' 199 | } 200 | ] 201 | } 202 | } 203 | }) 204 | $log = _$log_ 205 | 206 | lifecycle.beforeValidate.callCount = 0 207 | lifecycle.validate.callCount = 0 208 | lifecycle.afterValidate.callCount = 0 209 | lifecycle.beforeCreate.callCount = 0 210 | lifecycle.afterCreate.callCount = 0 211 | lifecycle.beforeUpdate.callCount = 0 212 | lifecycle.afterUpdate.callCount = 0 213 | lifecycle.beforeDestroy.callCount = 0 214 | lifecycle.afterDestroy.callCount = 0 215 | lifecycle.beforeInject.callCount = 0 216 | lifecycle.afterInject.callCount = 0 217 | lifecycle.serialize.callCount = 0 218 | lifecycle.deserialize.callCount = 0 219 | lifecycle.queryTransform.callCount = 0 220 | 221 | p1 = { author: 'John', age: 30, id: 5 } 222 | p2 = { author: 'Sally', age: 31, id: 6 } 223 | p3 = { author: 'Mike', age: 32, id: 7 } 224 | p4 = { author: 'Adam', age: 33, id: 8 } 225 | p5 = { author: 'Adam', age: 33, id: 9 } 226 | 227 | user1 = { 228 | name: 'John Anderson', 229 | id: 1, 230 | organizationId: 2 231 | } 232 | organization2 = { 233 | name: 'Test Corp 2', 234 | id: 2 235 | } 236 | comment3 = { 237 | content: 'test comment 3', 238 | id: 3, 239 | userId: 1 240 | } 241 | profile4 = { 242 | content: 'test profile 4', 243 | id: 4, 244 | userId: 1 245 | } 246 | 247 | comment11 = { 248 | id: 11, 249 | userId: 10, 250 | content: 'test comment 11' 251 | } 252 | comment12 = { 253 | id: 12, 254 | userId: 10, 255 | content: 'test comment 12' 256 | } 257 | comment13 = { 258 | id: 13, 259 | userId: 10, 260 | content: 'test comment 13' 261 | } 262 | organization14 = { 263 | id: 14, 264 | name: 'Test Corp' 265 | } 266 | profile15 = { 267 | id: 15, 268 | userId: 10, 269 | email: 'john.anderson@test.com' 270 | } 271 | user10 = { 272 | name: 'John Anderson', 273 | id: 10, 274 | organizationId: 14, 275 | comments: [ 276 | comment11, 277 | comment12, 278 | comment13 279 | ], 280 | organization: organization14, 281 | profile: profile15 282 | } 283 | user16 = { 284 | id: 16, 285 | organizationId: 15, 286 | name: 'test user 16' 287 | } 288 | user17 = { 289 | id: 17, 290 | organizationId: 15, 291 | name: 'test user 17' 292 | } 293 | user18 = { 294 | id: 18, 295 | organizationId: 15, 296 | name: 'test user 18' 297 | } 298 | organization15 = { 299 | name: 'Another Test Corp', 300 | id: 15, 301 | users: [ 302 | user16, 303 | user17, 304 | user18 305 | ] 306 | } 307 | user19 = { 308 | id: 19, 309 | name: 'test user 19' 310 | } 311 | user20 = { 312 | id: 20, 313 | name: 'test user 20' 314 | } 315 | comment19 = { 316 | content: 'test comment 19', 317 | id: 19, 318 | approvedBy: 19, 319 | approvedByUser: user19, 320 | userId: 20, 321 | user: user20 322 | } 323 | user22 = { 324 | id: 22, 325 | name: 'test user 22' 326 | } 327 | profile21 = { 328 | content: 'test profile 21', 329 | id: 21, 330 | userId: 22, 331 | user: user22 332 | } 333 | }) 334 | } 335 | 336 | // Clean up after each test 337 | afterEach(function () { 338 | try { 339 | $httpBackend.verifyNoOutstandingExpectation() 340 | $httpBackend.verifyNoOutstandingRequest() 341 | } catch (err) { 342 | console.log(err) 343 | } 344 | $log.reset() 345 | }) 346 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-angular", 3 | "description": "Angular wrapper for js-data.", 4 | "version": "3.2.4", 5 | "homepage": "http://www.js-data.io/docs/js-data-angular", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/js-data/js-data-angular.git" 9 | }, 10 | "author": "js-data-angular project authors", 11 | "license": "MIT", 12 | "main": "dist/js-data-angular.js", 13 | "standard": { 14 | "parser": "babel-eslint", 15 | "globals": [ 16 | "Headers", 17 | "fetch", 18 | "Request", 19 | "action", 20 | "describe", 21 | "it", 22 | "sinon", 23 | "before", 24 | "after", 25 | "beforeEach", 26 | "afterEach" 27 | ] 28 | }, 29 | "scripts": { 30 | "lint": "repo-tools lint karma.conf.js src/*.js scripts/*.js", 31 | "webpack": "node scripts/js-data-http.js && webpack --config webpack.config.js", 32 | "min": "uglifyjs dist/js-data-angular.js -o dist/js-data-angular.min.js --source-map dist/js-data-angular.min.map --source-map-url js-data-angular.min.map -v -m -c --screw-ie8", 33 | "version": "node scripts/version.js", 34 | "banner": "node scripts/banner.js", 35 | "gzip": "echo js-data-angular gzipped size: $(cat dist/js-data-angular.min.js | gzip -f9 | wc -c)kb", 36 | "build": "npm run lint && npm run webpack && npm run min && npm run version && npm run banner", 37 | "karma": "karma start", 38 | "test": "npm run build && npm run karma", 39 | "repo-tools": "repo-tools updates && repo-tools changelog && repo-tools authors", 40 | "release": "npm test && npm run repo-tools", 41 | "ci": "npm test && cat ./coverage/PhantomJS*/lcov.info | coveralls || true" 42 | }, 43 | "peerDependencies": { 44 | "angular": ">=1.1.0", 45 | "js-data": ">=2.0.0 <3" 46 | }, 47 | "devDependencies": { 48 | "babel-core": "6.7.2", 49 | "babel-loader": "6.2.4", 50 | "babel-polyfill": "6.7.2", 51 | "babel-preset-es2015": "6.6.0", 52 | "chai": "3.5.0", 53 | "coveralls": "2.11.8", 54 | "js-data-repo-tools": "0.2.0", 55 | "karma": "0.13.22", 56 | "karma-browserstack-launcher": "0.1.10", 57 | "karma-chai": "0.1.0", 58 | "karma-coverage": "0.5.5", 59 | "karma-mocha": "0.2.2", 60 | "karma-phantomjs-launcher": "1.0.2", 61 | "karma-sinon": "1.0.4", 62 | "mocha": "2.4.5", 63 | "phantomjs-prebuilt": "2.1.6", 64 | "request": "2.69.0", 65 | "sinon": "1.17.3", 66 | "tar.gz": "1.0.3", 67 | "tarball-extract": "0.0.3", 68 | "uglify-js": "2.6.2", 69 | "webpack": "1.12.14" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /scripts/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of js-data-http project authors. 2 | # 3 | # This file is controlled by scripts/authors.js 4 | # 5 | # Names are formatted as: 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | -------------------------------------------------------------------------------- /scripts/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # People who have contributed to the js-data-http project. 2 | # 3 | # This file is controlled by scripts/authors.js 4 | # 5 | # Names should be added to this file as: 6 | # [commit count] Name 7 | -------------------------------------------------------------------------------- /scripts/authors.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var exec = require('child_process').exec 3 | var path = require('path') 4 | 5 | console.log('Writing AUTHORS file...') 6 | 7 | var authorsFile = fs.readFileSync(path.join(__dirname, '/AUTHORS'), { 8 | encoding: 'utf-8' 9 | }) 10 | var contributorsFile = fs.readFileSync(path.join(__dirname, '/CONTRIBUTORS'), { 11 | encoding: 'utf-8' 12 | }) 13 | 14 | var tty = process.platform === 'win32' ? 'CON' : '/dev/tty' 15 | 16 | exec('git shortlog -s -e < ' + tty, function (err, stdout, stderr) { 17 | if (err) { 18 | console.error(err) 19 | process.exit(-1) 20 | } else { 21 | var lines = stdout.split('\n') 22 | var countsAndNames = lines.map(function (line) { 23 | return line.split('\t') 24 | }) 25 | var names = countsAndNames.map(function (pair) { 26 | return pair[1] 27 | }) 28 | 29 | // Add to or otherwise modify "names" if necessary 30 | 31 | fs.writeFileSync(path.join(__dirname, '/../AUTHORS'), authorsFile + names.join('\n'), { 32 | encoding: 'utf-8' 33 | }) 34 | fs.writeFileSync(path.join(__dirname, '/../fetch/AUTHORS'), authorsFile + names.join('\n'), { 35 | encoding: 'utf-8' 36 | }) 37 | fs.writeFileSync(path.join(__dirname, '/../node/AUTHORS'), authorsFile + names.join('\n'), { 38 | encoding: 'utf-8' 39 | }) 40 | console.log('Done!') 41 | console.log('Writing CONTRIBUTORS file...') 42 | 43 | names = lines 44 | 45 | // Add to or otherwise modify "names" if necessary 46 | 47 | fs.writeFileSync(path.join(__dirname, '/../CONTRIBUTORS'), contributorsFile + names.join('\n'), { 48 | encoding: 'utf-8' 49 | }) 50 | fs.writeFileSync(path.join(__dirname, '/../fetch/CONTRIBUTORS'), contributorsFile + names.join('\n'), { 51 | encoding: 'utf-8' 52 | }) 53 | fs.writeFileSync(path.join(__dirname, '/../node/CONTRIBUTORS'), contributorsFile + names.join('\n'), { 54 | encoding: 'utf-8' 55 | }) 56 | console.log('Done!') 57 | } 58 | }) 59 | -------------------------------------------------------------------------------- /scripts/banner.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var pkg = require('../package.json') 3 | 4 | var banner = '/*!\n' + 5 | '* js-data-angular\n' + 6 | '* @version ' + pkg.version + ' - Homepage \n' + 7 | '* @copyright (c) 2014-2016 js-data-angular project authors\n' + 8 | '* @license MIT \n' + 9 | '*\n' + 10 | '* @overview Angular wrapper for js-data.\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-angular.js') 27 | addBanner('dist/js-data-angular.min.js') 28 | 29 | console.log('Done!') 30 | -------------------------------------------------------------------------------- /scripts/cleanup.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | var pkg = require('../package.json') 4 | 5 | var path = './doc/js-data-angular/' + pkg.version + '/styles/' 6 | 7 | var files = fs.readdirSync(path) 8 | files.forEach(function (file) { 9 | if (file.indexOf('site') === 0) { 10 | if (file.indexOf('lumen') === -1 && file.indexOf('dibs') === -1) { 11 | fs.unlinkSync(path + file) 12 | } 13 | } 14 | }) 15 | 16 | path = './doc/js-data-angular/' + pkg.version 17 | 18 | files = fs.readdirSync(path) 19 | files.forEach(function (file) { 20 | if (file.indexOf('.html') === file.length - 5) { 21 | var content = fs.readFileSync(path + '/' + file, { encoding: 'utf8' }) 22 | content = content.replace(/\/home\/ubuntu\/workspace\//gi, '') 23 | fs.writeFileSync(path + '/' + file, content, { encoding: 'utf8' }) 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /scripts/js-data-http.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var url = 'https://github.com/js-data/js-data-http/archive/2.2.4.tar.gz' 3 | var directory = './.js-data-http/' 4 | var request = require('request') 5 | var targz = require('tar.gz') 6 | 7 | // Streams 8 | try { 9 | fs.statSync(directory) 10 | console.log('js-data-http already downloaded') 11 | } catch (err) { 12 | console.log('downloading js-data-http') 13 | var read = request.get(url) 14 | var write = targz().createWriteStream(directory) 15 | 16 | read.pipe(write) 17 | 18 | write.on('finish', function () { 19 | var copyRead = fs.createReadStream(directory + 'js-data-http-2.2.4/src/index.js') 20 | var copyWrite = fs.createWriteStream('./.js-data-http.js') 21 | copyRead.pipe(copyWrite) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /scripts/version.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var pkg = require('../package.json') 3 | 4 | console.log('Adding version to dist/ files...') 5 | 6 | function version (filepath) { 7 | var file = fs.readFileSync(filepath, { 8 | encoding: 'utf-8' 9 | }) 10 | 11 | file = file.replace(/<%= pkg\.version %>/gi, pkg.version) 12 | 13 | var parts = pkg.version.split('-') 14 | var numbers = parts[0].split('.') 15 | 16 | file = file.replace(/<%= major %>/gi, numbers[0]) 17 | file = file.replace(/<%= minor %>/gi, numbers[1]) 18 | file = file.replace(/<%= patch %>/gi, numbers[2]) 19 | 20 | if (pkg.version.indexOf('alpha') !== -1) { 21 | file = file.replace(/<%= alpha %>/gi, parts[1].replace('alpha.', '') + (parts.length > 2 ? '-' + parts[2] : '')) 22 | } else { 23 | file = file.replace(/<%= alpha %>/gi, false) 24 | } 25 | 26 | if (pkg.version.indexOf('beta') !== -1) { 27 | file = file.replace(/<%= beta %>/gi, parts[1].replace('beta.', '') + (parts.length > 2 ? '-' + parts[2] : '')) 28 | } else { 29 | file = file.replace(/<%= beta %>/gi, false) 30 | } 31 | 32 | fs.writeFileSync(filepath, file, { 33 | encoding: 'utf-8' 34 | }) 35 | } 36 | 37 | version('dist/js-data-angular.js') 38 | 39 | console.log('Done!') 40 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* jshint loopfunc:true */ 2 | let JSData = require('js-data') 3 | let DSHttpAdapter = require('../.js-data-http.js') 4 | let angular = require('angular') 5 | 6 | let { DSUtils, DSErrors } = JSData 7 | let { get, isString, isNumber, isObject, set, resolveId } = DSUtils 8 | 9 | let adapters = [ 10 | { 11 | project: 'js-data-localstorage', 12 | name: 'localstorage', 13 | 'class': 'DSLocalStorageAdapter' 14 | }, 15 | { 16 | project: 'js-data-localforage', 17 | name: 'localforage', 18 | 'class': 'DSLocalForageAdapter' 19 | }, 20 | { 21 | project: 'js-data-firebase', 22 | name: 'firebase', 23 | 'class': 'DSFirebaseAdapter' 24 | }, 25 | { 26 | project: 'js-data-sql', 27 | name: 'sql', 28 | 'class': 'DSSqlAdapter' 29 | } 30 | ] 31 | 32 | let functionsToWrap = [ 33 | 'compute', 34 | 'digest', 35 | 'eject', 36 | 'inject' 37 | ] 38 | 39 | function registerAdapter (adapter) { 40 | let Adapter 41 | 42 | try { 43 | Adapter = require(adapter.project) 44 | } catch (e) {} 45 | 46 | if (!Adapter) { 47 | Adapter = window[adapter.class] 48 | } 49 | 50 | if (Adapter) { 51 | adapter.loaded = true 52 | angular.module('js-data').provider(adapter.class, function () { 53 | let _this = this 54 | _this.defaults = {} 55 | _this.$get = [() => new Adapter(_this.defaults)] 56 | }) 57 | } 58 | } 59 | 60 | class DSHttpAdapterProvider { 61 | constructor () { 62 | let defaults = {} 63 | this.defaults = defaults 64 | 65 | this.$get = ['$http', 'DS', ($http, DS) => { 66 | defaults.http = defaults.http || $http 67 | let adapter = new DSHttpAdapter(defaults) 68 | DS.registerAdapter('http', adapter, { 'default': true }) 69 | return adapter 70 | }] 71 | } 72 | } 73 | 74 | class DSProvider { 75 | constructor () { 76 | let _this = this 77 | let deps = [] 78 | 79 | for (var i = 0; i < adapters.length; i++) { 80 | if (adapters[i].loaded) { 81 | deps.push(adapters[i].class) 82 | } 83 | } 84 | 85 | _this.defaults = {} 86 | 87 | JSData.DS.prototype.bindAll = function (resourceName, params, scope, expr, cb) { 88 | let _this = this 89 | 90 | params = params || {} 91 | 92 | if (!_this.definitions[resourceName]) { 93 | throw new DSErrors.NER(resourceName) 94 | } else if (!isObject(params)) { 95 | throw new DSErrors.IA('"params" must be an object!') 96 | } else if (!isObject(scope)) { 97 | throw new DSErrors.IA('"scope" must be an object!') 98 | } else if (!isString(expr)) { 99 | throw new DSErrors.IA('"expr" must be a string!') 100 | } 101 | 102 | let idAttribute = _this.definitions[resourceName].idAttribute 103 | 104 | try { 105 | return scope.$watch(() => _this.lastModified(resourceName), () => { 106 | let items = _this.filter(resourceName, params) 107 | if (items && items.length) { 108 | angular.forEach(items, (item) => { 109 | _this.compute(resourceName, get(item, idAttribute)) 110 | }) 111 | } 112 | set(scope, expr, items) 113 | if (cb) { 114 | cb(null, items) 115 | } 116 | }) 117 | } catch (err) { 118 | if (cb) { 119 | cb(err) 120 | } else { 121 | throw err 122 | } 123 | } 124 | } 125 | 126 | JSData.DS.prototype.bindOne = function (resourceName, id, scope, expr, cb) { 127 | let _this = this 128 | 129 | id = resolveId(_this.definitions[resourceName], id) 130 | if (!_this.definitions[resourceName]) { 131 | throw new DSErrors.NER(resourceName) 132 | } else if (!isString(id) && !isNumber(id)) { 133 | throw new DSErrors.IA('"id" must be a string or a number!') 134 | } else if (!isObject(scope)) { 135 | throw new DSErrors.IA('"scope" must be an object!') 136 | } else if (!isString(expr)) { 137 | throw new DSErrors.IA('"expr" must be a string!') 138 | } 139 | 140 | try { 141 | return scope.$watch(() => _this.lastModified(resourceName, id), () => { 142 | let item = _this.get(resourceName, id) 143 | if (item) { 144 | _this.compute(resourceName, id) 145 | } 146 | set(scope, expr, item) 147 | if (cb) { 148 | cb(null, item) 149 | } 150 | }) 151 | } catch (err) { 152 | if (cb) { 153 | cb(err) 154 | } else { 155 | throw err 156 | } 157 | } 158 | } 159 | 160 | function load (...args) { 161 | let $rootScope = args[args.length - 2] 162 | let $q = args[args.length - 1] 163 | let store = new JSData.DS(_this.defaults) 164 | let originals = {} 165 | 166 | function QPromise (executor) { 167 | let deferred = $q.defer() 168 | 169 | try { 170 | executor( 171 | angular.bind(deferred, deferred.resolve), 172 | angular.bind(deferred, deferred.reject) 173 | ) 174 | } catch (err) { 175 | deferred.reject(err) 176 | } 177 | 178 | return deferred.promise 179 | } 180 | 181 | QPromise.all = $q.all 182 | QPromise.when = $q.when 183 | QPromise.reject = $q.reject 184 | 185 | DSUtils.Promise = QPromise 186 | 187 | // Register any adapters that have been loaded 188 | if (args.length) { 189 | for (var i = 0; i < args.length; i++) { 190 | for (var j = 0; j < adapters.length; j++) { 191 | if (adapters[j].loaded && !adapters[j].registered) { 192 | adapters[j].registered = true 193 | store.registerAdapter(adapters[j].name, args[i]) 194 | break 195 | } 196 | } 197 | } 198 | } 199 | 200 | // Wrap certain sync functions with $apply 201 | for (var k = 0; k < functionsToWrap.length; k++) { 202 | let name = functionsToWrap[k] 203 | originals[name] = store[name] 204 | store[name] = (...args) => { 205 | if (!$rootScope.$$phase) { 206 | return $rootScope.$apply(() => originals[name].apply(store, args)) 207 | } 208 | return originals[name].apply(store, args) 209 | } 210 | } 211 | 212 | // Hook into the digest loop 213 | if (typeof Object.observe !== 'function' || typeof Array.observe !== 'function') { 214 | $rootScope.$watch(() => store.observe.Platform.performMicrotaskCheckpoint()) 215 | } 216 | 217 | return store 218 | } 219 | 220 | deps.push('$rootScope') 221 | deps.push('$q') 222 | deps.push(load) 223 | 224 | _this.$get = deps 225 | } 226 | } 227 | angular.module('js-data', ['ng']) 228 | .value('DSUtils', DSUtils) 229 | .value('DSErrors', DSErrors) 230 | .value('DSVersion', JSData.version) 231 | .provider('DS', DSProvider) 232 | .provider('DSHttpAdapter', DSHttpAdapterProvider) 233 | .run(['DS', 'DSHttpAdapter', (DS, DSHttpAdapter) => { 234 | DS.registerAdapter('http', DSHttpAdapter, { 'default': true }) 235 | }]) 236 | 237 | for (var i = 0; i < adapters.length; i++) { 238 | registerAdapter(adapters[i]) 239 | } 240 | 241 | // return the module name 242 | module.exports = 'js-data' 243 | try { 244 | module.exports.name = 'js-data' 245 | } catch (e) {} 246 | -------------------------------------------------------------------------------- /test/adapters/http/create.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.create', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a POST request', function () { 6 | $httpBackend.expectPOST('http://test.angular-cache.com/posts', { 7 | author: 'John', 8 | age: 30 9 | }).respond(200, p1); 10 | 11 | DSHttpAdapter.create(Post, { author: 'John', age: 30 }).then(function (data) { 12 | assert.deepEqual(data, p1, 'post should have been created'); 13 | }, function (err) { 14 | console.error(err.stack); 15 | fail('should not have rejected'); 16 | }); 17 | 18 | $httpBackend.flush(); 19 | 20 | $httpBackend.expectPOST('api2/posts', { 21 | author: 'John', 22 | age: 30 23 | }).respond(200, p1); 24 | 25 | DSHttpAdapter.create(Post, { author: 'John', age: 30 }, { basePath: 'api2' }).then(function (data) { 26 | assert.deepEqual(data, p1, 'post should have been created'); 27 | }, function (err) { 28 | console.error(err.stack); 29 | fail('should not have rejected'); 30 | }); 31 | 32 | $httpBackend.flush(); 33 | 34 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called twice'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/adapters/http/destroy.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.destroy', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a DELETE request', function () { 6 | $httpBackend.expectDELETE('http://test.angular-cache.com/posts/1').respond(200, 1); 7 | 8 | DSHttpAdapter.destroy(Post, 1).then(function (data) { 9 | assert.deepEqual(data, 1, 'post should have been deleted'); 10 | }, function (err) { 11 | console.error(err.stack); 12 | fail('should not have rejected'); 13 | }); 14 | 15 | $httpBackend.flush(); 16 | 17 | $httpBackend.expectDELETE('api2/posts/1').respond(200, 1); 18 | 19 | DSHttpAdapter.destroy(Post, 1, { basePath: 'api2' }).then(function (data) { 20 | assert.deepEqual(data, 1, 'post should have been deleted'); 21 | }, function (err) { 22 | console.error(err.stack); 23 | fail('should not have rejected'); 24 | }); 25 | 26 | $httpBackend.flush(); 27 | 28 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called twice'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/adapters/http/destroyAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.destroyAll', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a DELETE request', function () { 6 | $httpBackend.expectDELETE('http://test.angular-cache.com/posts').respond(204); 7 | 8 | DSHttpAdapter.destroyAll(Post, {}).then(function (data) { 9 | assert.isUndefined(data, 'posts should have been found'); 10 | }, function (err) { 11 | console.error(err.stack); 12 | fail('should not have rejected'); 13 | }); 14 | 15 | $httpBackend.flush(); 16 | 17 | $httpBackend.expectDELETE('api2/posts?where=%7B%22author%22:%7B%22%3D%3D%22:%22John%22%7D%7D').respond(204); 18 | 19 | DSHttpAdapter.destroyAll(Post, { 20 | where: { 21 | author: { 22 | '==': 'John' 23 | } 24 | } 25 | }, { basePath: 'api2' }).then(function (data) { 26 | assert.isUndefined(data, 'posts should have been destroyed'); 27 | }, function (err) { 28 | console.error(err.stack); 29 | fail('should not have rejected'); 30 | }); 31 | 32 | $httpBackend.flush(); 33 | 34 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/adapters/http/find.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.find', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a GET request', function () { 6 | $httpBackend.expectGET('http://test.angular-cache.com/posts/1').respond(200, p1); 7 | 8 | DSHttpAdapter.find(Post, 1).then(function (data) { 9 | assert.deepEqual(data, p1, 'post should have been found'); 10 | }, function (err) { 11 | console.error(err.stack); 12 | fail('should not have rejected'); 13 | }); 14 | 15 | $httpBackend.flush(); 16 | 17 | $httpBackend.expectGET('api2/posts/1').respond(200, p1); 18 | 19 | DSHttpAdapter.find(Post, 1, { basePath: 'api2' }).then(function (data) { 20 | assert.deepEqual(data, p1, 'post should have been found'); 21 | }, function (err) { 22 | console.error(err.stack); 23 | fail('should not have rejected'); 24 | }); 25 | 26 | $httpBackend.flush(); 27 | 28 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called twice'); 29 | }); 30 | 31 | it('should error if the item is undefined', function () { 32 | $httpBackend.expectGET('http://test.angular-cache.com/posts/1').respond(200); 33 | 34 | DSHttpAdapter.find(Post, 1).then(function () { 35 | fail('should not have reached this'); 36 | }).catch(function (err) { 37 | assert.equal(err.message, 'Not Found!'); 38 | return true; 39 | }); 40 | 41 | $httpBackend.flush(); 42 | }); 43 | 44 | it('should use default configs', function () { 45 | $httpBackend.expectGET('http://test.angular-cache.com/posts/1?test=test', { 46 | Authorization: 'test', 47 | Accept: 'application/json, text/plain, */*' 48 | }).respond(200, p1); 49 | 50 | DSHttpAdapter.defaults.httpConfig.params = { test: 'test' }; 51 | DSHttpAdapter.defaults.httpConfig.headers = { Authorization: 'test' }; 52 | 53 | DSHttpAdapter.find(Post, 1).then(function (data) { 54 | assert.deepEqual(data, p1, 'post should have been found'); 55 | }, function (err) { 56 | console.error(err.stack); 57 | fail('should not have rejected'); 58 | }); 59 | 60 | $httpBackend.flush(); 61 | 62 | delete DSHttpAdapter.defaults.httpConfig.params; 63 | delete DSHttpAdapter.defaults.httpConfig.headers; 64 | }); 65 | 66 | it('should use suffixes', function () { 67 | var Thing = DS.defineResource({ 68 | name: 'thing', 69 | endpoint: 'things', 70 | suffix: '.xml' 71 | }); 72 | 73 | DSHttpAdapter.defaults.suffix = '.json'; 74 | 75 | $httpBackend.expectGET('http://test.angular-cache.com/things/1.xml').respond(200, { id: 1 }); 76 | 77 | DSHttpAdapter.find(Thing, 1); 78 | 79 | $httpBackend.expectGET('http://test.angular-cache.com/posts/1.json').respond(200, { id: 1 }); 80 | 81 | DSHttpAdapter.find(Post, 1); 82 | 83 | $httpBackend.flush(); 84 | 85 | DSHttpAdapter.defaults.suffix = ''; 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/adapters/http/findAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.findAll', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a GET request', function () { 6 | $httpBackend.expectGET('http://test.angular-cache.com/posts').respond(200, [p1]); 7 | 8 | DSHttpAdapter.findAll(Post, {}).then(function (data) { 9 | assert.deepEqual(data, [p1], 'posts should have been found'); 10 | }, function (err) { 11 | console.error(err.stack); 12 | fail('should not have rejected'); 13 | }); 14 | 15 | $httpBackend.flush(); 16 | 17 | $httpBackend.expectGET('api2/posts?where=%7B%22author%22:%7B%22%3D%3D%22:%22John%22%7D%7D').respond(200, [p1]); 18 | 19 | DSHttpAdapter.findAll(Post, { 20 | where: { 21 | author: { 22 | '==': 'John' 23 | } 24 | } 25 | }, { basePath: 'api2' }).then(function (data) { 26 | assert.deepEqual(data, [p1], 'posts should have been found'); 27 | }, function (err) { 28 | console.error(err.stack); 29 | fail('should not have rejected'); 30 | }); 31 | 32 | $httpBackend.flush(); 33 | 34 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/adapters/http/update.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.update', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a PUT request', function () { 6 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/1', { 7 | author: 'John', 8 | age: 30 9 | }).respond(200, p1); 10 | 11 | DSHttpAdapter.update(Post, 1, { author: 'John', age: 30 }).then(function (data) { 12 | assert.deepEqual(data, p1, 'post 5 should have been updated'); 13 | }, function (err) { 14 | console.error(err.stack); 15 | fail('should not have rejected'); 16 | }); 17 | 18 | $httpBackend.flush(); 19 | 20 | $httpBackend.expectPUT('api2/posts/1', { 21 | author: 'John', 22 | age: 30 23 | }).respond(200, p1); 24 | 25 | DSHttpAdapter.update(Post, 1, { author: 'John', age: 30 }, { basePath: 'api2' }).then(function (data) { 26 | assert.deepEqual(data, p1, 'post 5 should have been updated'); 27 | }, function (err) { 28 | console.error(err.stack); 29 | fail('should not have rejected'); 30 | }); 31 | 32 | $httpBackend.flush(); 33 | 34 | assert.equal(lifecycle.queryTransform.callCount, 2, 'queryTransform should have been called twice'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/adapters/http/updateAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DSHttpAdapter.updateAll', function () { 2 | 3 | beforeEach(startInjector); 4 | 5 | it('should make a PUT request', function () { 6 | $httpBackend.expectPUT('http://test.angular-cache.com/posts').respond(200, [p1]); 7 | 8 | DSHttpAdapter.updateAll(Post, { author: 'John', age: 30 }).then(function (data) { 9 | assert.deepEqual(data, [p1], 'posts should have been updated'); 10 | }, function (err) { 11 | console.error(err.stack); 12 | fail('should not have rejected'); 13 | }); 14 | 15 | $httpBackend.flush(); 16 | 17 | $httpBackend.expectPUT('api2/posts?where=%7B%22author%22:%7B%22%3D%3D%22:%22John%22%7D%7D').respond(200, [p1]); 18 | 19 | DSHttpAdapter.updateAll(Post, { author: 'John', age: 30 }, { 20 | where: { 21 | author: { 22 | '==': 'John' 23 | } 24 | } 25 | }, { basePath: 'api2' }).then(function (data) { 26 | assert.deepEqual(data, [p1], 'posts should have been updated'); 27 | }, function (err) { 28 | console.error(err.stack); 29 | fail('should not have rejected'); 30 | }); 31 | 32 | $httpBackend.flush(); 33 | 34 | assert.equal(lifecycle.queryTransform.callCount, 1, 'queryTransform should have been called'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/datastore/async_methods/create.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.create', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should create an item and save it to the server', function () { 5 | $httpBackend.expectPOST('http://test.angular-cache.com/posts').respond(200, p1); 6 | 7 | DS.create('post', { author: 'John', age: 30 }).then(function (post) { 8 | assert.deepEqual(angular.toJson(post), angular.toJson(p1), 'post 5 should have been created'); 9 | 10 | assert.equal(lifecycle.beforeCreate.callCount, 1, 'beforeCreate should have been called'); 11 | assert.equal(lifecycle.afterCreate.callCount, 1, 'afterCreate should have been called'); 12 | assert.equal(lifecycle.beforeInject.callCount, 1, 'beforeInject should have been called'); 13 | assert.equal(lifecycle.afterInject.callCount, 1, 'afterInject should have been called'); 14 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 15 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 16 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson(p1)); 17 | }, function (err) { 18 | console.error(err.stack); 19 | fail('should not have rejected'); 20 | }); 21 | 22 | $httpBackend.flush(); 23 | }); 24 | it('should create an item and save it to the server but not inject the result', function () { 25 | DSHttpAdapter.defaults.forceTrailingSlash = true; 26 | $httpBackend.expectPOST('http://test.angular-cache.com/posts/').respond(200, p1); 27 | 28 | DS.create('post', { author: 'John', age: 30 }, { cacheResponse: false }).then(function (post) { 29 | assert.deepEqual(angular.toJson(post), angular.toJson(p1), 'post 5 should have been created'); 30 | }, function (err) { 31 | console.error(err.stack); 32 | fail('should not have rejected'); 33 | }); 34 | 35 | $httpBackend.flush(); 36 | 37 | DSHttpAdapter.defaults.forceTrailingSlash = false; 38 | 39 | assert.equal(lifecycle.beforeCreate.callCount, 1, 'beforeCreate should have been called'); 40 | assert.equal(lifecycle.afterCreate.callCount, 1, 'afterCreate should have been called'); 41 | assert.equal(lifecycle.beforeInject.callCount, 0, 'beforeInject should not have been called'); 42 | assert.equal(lifecycle.afterInject.callCount, 0, 'afterInject should not have been called'); 43 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 44 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 45 | assert.isUndefined(DS.get('post', 5)); 46 | }); 47 | it('should work with the upsert option', function () { 48 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, p1); 49 | 50 | DS.create('post', { author: 'John', age: 30, id: 5 }).then(function (post) { 51 | assert.deepEqual(angular.toJson(post), angular.toJson(p1), 'post 5 should have been created'); 52 | }, function (err) { 53 | console.error(err.stack); 54 | fail('should not have rejected'); 55 | }); 56 | 57 | $httpBackend.flush(); 58 | $httpBackend.expectPOST('http://test.angular-cache.com/posts').respond(200, p2); 59 | 60 | DS.create('post', { author: 'Sue', age: 70, id: 6 }, { upsert: false }).then(function (post) { 61 | assert.deepEqual(angular.toJson(post), angular.toJson(p2), 'post 6 should have been created'); 62 | }, function (err) { 63 | console.error(err.stack); 64 | fail('should not have rejected'); 65 | }); 66 | 67 | $httpBackend.flush(); 68 | 69 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 70 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 71 | assert.equal(lifecycle.beforeCreate.callCount, 1, 'beforeCreate should have been called'); 72 | assert.equal(lifecycle.afterCreate.callCount, 1, 'afterCreate should have been called'); 73 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called twice'); 74 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called twice'); 75 | assert.equal(lifecycle.serialize.callCount, 2, 'serialize should have been called twice'); 76 | assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called twice'); 77 | assert.isDefined(DS.get('post', 5)); 78 | assert.isDefined(DS.get('post', 6)); 79 | }); 80 | it('should create an item that includes relations, save them to the server and inject the results', function () { 81 | var payload = { 82 | id: 99, 83 | name: 'Sally', 84 | profile: { 85 | id: 999, 86 | userId: 99, 87 | email: 'sally@test.com' 88 | } 89 | }; 90 | 91 | $httpBackend.expectPOST('http://test.angular-cache.com/user').respond(200, payload); 92 | 93 | DS.create('user', { 94 | name: 'Sally', 95 | profile: { 96 | email: 'sally@test.com' 97 | } 98 | }, { 99 | findBelongsTo: true 100 | }).then(function (user) { 101 | assert.deepEqual(user.id, payload.id, 'user should have been created'); 102 | 103 | DS.find('user', 99); // should not trigger another http request 104 | }, function (err) { 105 | console.error(err.stack); 106 | fail('should not have rejected'); 107 | }); 108 | 109 | $httpBackend.flush(); 110 | 111 | assert.equal(lifecycle.beforeCreate.callCount, 1, 'beforeCreate should have been called twice'); 112 | assert.equal(lifecycle.afterCreate.callCount, 1, 'afterCreate should have been called twice'); 113 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called twice'); 114 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called twice'); 115 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 116 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 117 | assert.deepEqual(DS.get('user', 99).id, payload.id); 118 | assert.isObject(DS.get('user', 99).profile); 119 | assert.deepEqual(DS.get('profile', 999).id, 999); 120 | assert.isObject(DS.get('profile', 999).user); 121 | }); 122 | it('should handle nested resources', function () { 123 | var testComment = { 124 | id: 5, 125 | content: 'test', 126 | approvedBy: 4 127 | }; 128 | var testComment2 = { 129 | id: 6, 130 | content: 'test', 131 | approvedBy: 4 132 | }; 133 | $httpBackend.expectPOST('http://test.angular-cache.com/user/4/comment').respond(200, testComment); 134 | 135 | DS.create('comment', { 136 | content: 'test', 137 | approvedBy: 4 138 | }).then(function (comment) { 139 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment)); 140 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 5))); 141 | }, function () { 142 | fail('Should not have failed!'); 143 | }); 144 | 145 | $httpBackend.flush(); 146 | $httpBackend.expectPOST('http://test.angular-cache.com/user/4/comment').respond(200, testComment2); 147 | 148 | DS.create('comment', { 149 | content: 'test' 150 | }, { 151 | params: { 152 | approvedBy: 4 153 | } 154 | }).then(function (comment) { 155 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment2)); 156 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 6))); 157 | }, function () { 158 | fail('Should not have failed!'); 159 | }); 160 | 161 | $httpBackend.flush(); 162 | $httpBackend.expectPOST('http://test.angular-cache.com/comment').respond(200, testComment2); 163 | 164 | DS.create('comment', { 165 | content: 'test', 166 | approvedBy: 4 167 | }, { 168 | params: { 169 | approvedBy: false 170 | } 171 | }).then(function (comment) { 172 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment2)); 173 | assert.deepEqual(comment, DS.get('comment', 6)); 174 | }, function () { 175 | fail('Should not have failed!'); 176 | }); 177 | $httpBackend.flush(); 178 | }); 179 | it('should find inverse links', function () { 180 | var organization = DS.inject('organization', { 181 | id: 77 182 | }); 183 | 184 | $httpBackend.expectPOST('http://test.angular-cache.com/organization/77/user').respond(200, { 185 | organizationId: 77, 186 | id: 88 187 | }); 188 | 189 | DS.create('user', { 190 | organizationId: 77, 191 | id: 88 192 | }, { upsert: false }).then(function (user) { 193 | assert.isArray(organization.users); 194 | assert.equal(1, organization.users.length); 195 | assert.isObject(user.organization); 196 | assert.isTrue(user.organization === organization); 197 | assert.isTrue(user === organization.users[0]); 198 | }, function () { 199 | fail('Should not have failed!'); 200 | }); 201 | 202 | $httpBackend.flush(); 203 | }); 204 | // Not yet implemented in js-data 205 | //it('should eager inject', function () { 206 | // $httpBackend.expectPOST('http://test.angular-cache.com/organization/77/user').respond(200, { 207 | // organizationId: 77, 208 | // id: 88 209 | // }); 210 | // 211 | // var eagerUser; 212 | // 213 | // DS.create('user', { 214 | // organizationId: 77 215 | // }, { eagerInject: true }).then(function (user) { 216 | // assert.equal(user.id, 88); 217 | // assert.isTrue(eagerUser === user); 218 | // assert.isTrue(DS.filter('user')[0] === user); 219 | // }, function () { 220 | // fail('Should not have succeeded!'); 221 | // }); 222 | // 223 | // $rootScope.$apply(); 224 | // 225 | // eagerUser = DS.filter('user')[0]; 226 | // assert.isDefined(eagerUser); 227 | // assert.equal(eagerUser.organizationId, 77); 228 | // assert.notEqual(eagerUser.id, 88); 229 | // 230 | // $httpBackend.flush(); 231 | //}); 232 | }); 233 | -------------------------------------------------------------------------------- /test/datastore/async_methods/destroy.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.destroy', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should delete an item from the data store', function () { 5 | $httpBackend.expectDELETE('http://test.angular-cache.com/posts/5').respond(200, 5); 6 | 7 | DS.inject('post', p1); 8 | 9 | DS.destroy('post', 5).then(function (id) { 10 | assert.equal(id, 5, 'post 5 should have been deleted'); 11 | assert.equal(lifecycle.beforeDestroy.callCount, 1, 'beforeDestroy should have been called'); 12 | assert.equal(lifecycle.afterDestroy.callCount, 1, 'afterDestroy should have been called'); 13 | assert.isUndefined(DS.get('post', 5)); 14 | assert.equal(DS.lastModified('post', 5), 0); 15 | assert.equal(DS.lastSaved('post', 5), 0); 16 | }, function (err) { 17 | console.error(err.stack); 18 | fail('should not have rejected'); 19 | }); 20 | 21 | $httpBackend.flush(); 22 | }); 23 | it('should handle nested resources', function () { 24 | var testComment = { 25 | id: 5, 26 | content: 'test' 27 | }; 28 | var testComment2 = { 29 | id: 6, 30 | content: 'test', 31 | approvedBy: 4 32 | }; 33 | 34 | DS.inject('comment', testComment); 35 | 36 | $httpBackend.expectDELETE('http://test.angular-cache.com/user/4/comment/5').respond(204); 37 | 38 | DS.destroy('comment', 5, { 39 | params: { 40 | approvedBy: 4 41 | } 42 | }).then(null, function () { 43 | fail('Should not have failed!'); 44 | }); 45 | 46 | $httpBackend.flush(); 47 | 48 | $httpBackend.expectDELETE('http://test.angular-cache.com/user/4/comment/6').respond(204); 49 | 50 | DS.inject('comment', testComment2); 51 | 52 | DS.destroy('comment', 6, { 53 | bypassCache: true 54 | }).then(null, function () { 55 | fail('Should not have failed!'); 56 | }); 57 | 58 | $httpBackend.flush(); 59 | 60 | $httpBackend.expectDELETE('http://test.angular-cache.com/comment/6').respond(204); 61 | DS.inject('comment', testComment2); 62 | DS.destroy('comment', 6, { 63 | params: { 64 | approvedBy: false 65 | } 66 | }).then(null, function (err) { 67 | console.log(err.stack); 68 | fail('Should not have failed!'); 69 | }); 70 | 71 | $httpBackend.flush(); 72 | }); 73 | // not yet implemented in js-data 74 | //it('should eager eject', function (done) { 75 | // $httpBackend.expectDELETE('http://test.angular-cache.com/posts/5').respond(200, 5); 76 | // 77 | // DS.inject('post', p1); 78 | // 79 | // DS.destroy('post', 5, { eagerEject: true }).then(function (id) { 80 | // assert.equal(id, 5, 'post 5 should have been deleted'); 81 | // }, function (err) { 82 | // console.error(err.stack); 83 | // fail('should not have rejected'); 84 | // }); 85 | // 86 | // $rootScope.$apply(); 87 | // 88 | // assert.isUndefined(DS.get('post', 5)); 89 | // 90 | // setTimeout(function () { 91 | // try { 92 | // $httpBackend.flush(); 93 | // 94 | // setTimeout(function () { 95 | // try { 96 | // assert.equal(lifecycle.beforeDestroy.callCount, 1, 'beforeDestroy should have been called'); 97 | // assert.equal(lifecycle.afterDestroy.callCount, 1, 'afterDestroy should have been called'); 98 | // assert.isUndefined(DS.get('post', 5)); 99 | // assert.equal(DS.lastModified('post', 5), 0); 100 | // assert.equal(DS.lastSaved('post', 5), 0); 101 | // 102 | // done(); 103 | // } catch (e) { 104 | // done(e); 105 | // } 106 | // }); 107 | // } catch (e) { 108 | // done(e); 109 | // } 110 | // }, 30); 111 | //}); 112 | }); 113 | -------------------------------------------------------------------------------- /test/datastore/async_methods/destroyAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.destroyAll', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should query the server for a collection', function () { 5 | $httpBackend.expectDELETE('http://test.angular-cache.com/posts?where=%7B%22age%22:33%7D').respond(200); 6 | 7 | DS.inject('post', p1); 8 | DS.inject('post', p2); 9 | DS.inject('post', p3); 10 | DS.inject('post', p4); 11 | DS.inject('post', p5); 12 | 13 | DS.destroyAll('post', { where: { age: 33 } }).then(null, function (err) { 14 | console.error(err.stack); 15 | fail('Should not have rejected!'); 16 | }); 17 | 18 | $httpBackend.flush(); 19 | 20 | assert.isDefined(DS.get('post', 5)); 21 | assert.isDefined(DS.get('post', 6)); 22 | assert.isDefined(DS.get('post', 7)); 23 | assert.isUndefined(DS.get('post', 8)); 24 | assert.isUndefined(DS.get('post', 9)); 25 | 26 | $httpBackend.expectDELETE('http://test.angular-cache.com/posts').respond(200); 27 | 28 | DS.inject('post', p1); 29 | DS.inject('post', p2); 30 | DS.inject('post', p3); 31 | DS.inject('post', p4); 32 | DS.inject('post', p5); 33 | 34 | DS.destroyAll('post', {}).then(null, function (err) { 35 | console.error(err.stack); 36 | fail('Should not have rejected!'); 37 | }); 38 | 39 | $httpBackend.flush(); 40 | 41 | assert.deepEqual(DS.filter('post', {}), [], 'The posts should not be in the store yet'); 42 | }); 43 | it('should handle nested resources', function () { 44 | $httpBackend.expectDELETE('http://test.angular-cache.com/user/4/comment?content=test').respond(204); 45 | 46 | DS.destroyAll('comment', { 47 | content: 'test' 48 | }, { 49 | params: { 50 | approvedBy: 4 51 | } 52 | }).then(function () { 53 | }, function (err) { 54 | console.log(err); 55 | fail('Should not have failed!'); 56 | }); 57 | 58 | $httpBackend.flush(); 59 | 60 | $httpBackend.expectDELETE('http://test.angular-cache.com/comment?content=test').respond(204); 61 | 62 | DS.destroyAll('comment', { 63 | content: 'test' 64 | }).then(function () { 65 | }, function (err) { 66 | console.log(err); 67 | fail('Should not have failed!'); 68 | }); 69 | 70 | $httpBackend.flush(); 71 | 72 | $httpBackend.expectDELETE('http://test.angular-cache.com/comment?content=test').respond(204); 73 | 74 | DS.destroyAll('comment', { 75 | content: 'test' 76 | }, { 77 | params: { 78 | approvedBy: false 79 | } 80 | }).then(function () { 81 | }, function (err) { 82 | console.log(err); 83 | fail('Should not have failed!'); 84 | }); 85 | 86 | $httpBackend.flush(); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/datastore/async_methods/find.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.find', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should get an item from the server', function () { 5 | $httpBackend.expectGET('http://test.angular-cache.com/posts/5').respond(200, p1); 6 | 7 | DS.find('post', 5).then(function (post) { 8 | assert.deepEqual(angular.toJson(post), angular.toJson(p1)); 9 | }, function (err) { 10 | console.error(err.stack); 11 | fail('Should not have rejected!'); 12 | }); 13 | 14 | assert.isUndefined(DS.get('post', 5), 'The post should not be in the store yet'); 15 | 16 | // Should have no effect because there is already a pending query 17 | DS.find('post', 5).then(function (post) { 18 | assert.deepEqual(angular.toJson(post), angular.toJson(p1)); 19 | }, function (err) { 20 | console.error(err.stack); 21 | fail('Should not have rejected!'); 22 | }); 23 | 24 | $httpBackend.flush(); 25 | 26 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson(p1), 'The post is now in the store'); 27 | assert.isNumber(DS.lastModified('post', 5)); 28 | assert.isNumber(DS.lastSaved('post', 5)); 29 | 30 | // Should not make a request because the request was already completed 31 | DS.find('post', 5).then(function (post) { 32 | assert.deepEqual(angular.toJson(post), angular.toJson(p1)); 33 | }, function (err) { 34 | console.error(err.stack); 35 | fail('Should not have rejected!'); 36 | }); 37 | 38 | $httpBackend.expectGET('http://test.angular-cache.com/posts/5').respond(200, p1); 39 | 40 | // Should make a request because bypassCache is set to true 41 | DS.find('post', 5, { bypassCache: true }).then(function (post) { 42 | assert.deepEqual(angular.toJson(post), angular.toJson(p1)); 43 | }, function (err) { 44 | console.error(err.stack); 45 | fail('Should not have rejected!'); 46 | }); 47 | 48 | $httpBackend.flush(); 49 | 50 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 51 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 52 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 53 | assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called'); 54 | }); 55 | it('should get an item from the server but not store it if cacheResponse is false', function () { 56 | $httpBackend.expectGET('http://test.angular-cache.com/posts/5').respond(200, p1); 57 | 58 | DS.find('post', 5, { cacheResponse: false }).then(function (post) { 59 | assert.deepEqual(angular.toJson(post), angular.toJson(p1)); 60 | }, function (err) { 61 | console.error(err.stack); 62 | fail('Should not have rejected!'); 63 | }); 64 | 65 | $httpBackend.flush(); 66 | 67 | assert.isUndefined(DS.get('post', 5), 'The post should not have been injected into the store'); 68 | assert.equal(lifecycle.beforeInject.callCount, 0, 'beforeInject should have been called'); 69 | assert.equal(lifecycle.afterInject.callCount, 0, 'afterInject should have been called'); 70 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 71 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 72 | }); 73 | it('should correctly propagate errors', function () { 74 | $httpBackend.expectGET('http://test.angular-cache.com/posts/5').respond(404, 'Not Found'); 75 | 76 | DS.find('post', 5).then(function () { 77 | fail('should not have succeeded!'); 78 | }); 79 | 80 | try { 81 | $httpBackend.flush(); 82 | } catch (err) { 83 | assert.equal(err.data, 'Not Found'); 84 | } 85 | }); 86 | it('should handle nested resources', function () { 87 | var testComment = { 88 | id: 5, 89 | content: 'test', 90 | approvedBy: 4 91 | }; 92 | $httpBackend.expectGET('http://test.angular-cache.com/user/4/comment/5').respond(200, testComment); 93 | 94 | DS.find('comment', 5, { 95 | params: { 96 | approvedBy: 4 97 | } 98 | }).then(function (comment) { 99 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment)); 100 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 5))); 101 | }, function () { 102 | fail('Should not have failed!'); 103 | }); 104 | 105 | $httpBackend.flush(); 106 | 107 | $httpBackend.expectGET('http://test.angular-cache.com/user/4/comment/5').respond(200, testComment); 108 | 109 | DS.find('comment', 5, { 110 | bypassCache: true 111 | }).then(function (comment) { 112 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment)); 113 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 5))); 114 | }, function () { 115 | fail('Should not have failed!'); 116 | }); 117 | 118 | $httpBackend.flush(); 119 | 120 | $httpBackend.expectGET('http://test.angular-cache.com/comment/5').respond(200, testComment); 121 | 122 | DS.find('comment', 5, { 123 | bypassCache: true, 124 | params: { 125 | approvedBy: false 126 | } 127 | }).then(function (comment) { 128 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment)); 129 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 5))); 130 | }, function () { 131 | fail('Should not have failed!'); 132 | }); 133 | 134 | $httpBackend.flush(); 135 | 136 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user/19/comment/19').respond(200, comment19); 137 | 138 | DS.find('comment', 19, { 139 | bypassCache: true, 140 | params: { 141 | approvedBy: 19, 142 | organizationId: 14 143 | } 144 | }).then(function (comment) { 145 | assert.equal(comment.id, comment19.id); 146 | assert.equal(comment.id, DS.get('comment', 19).id); 147 | }, function () { 148 | fail('Should not have failed!'); 149 | }); 150 | 151 | $httpBackend.flush(); 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/datastore/async_methods/findAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.findAll', function () { 2 | 3 | function createComments(iterations) { 4 | var comments = []; 5 | for (var i = 1; i < iterations; i++) { 6 | var comment = { 7 | id: i 8 | }; 9 | for (var j = 1; j < 40; j++) { 10 | comment['thing_' + j] = 'test_content_' + j; 11 | } 12 | comments.push(comment); 13 | } 14 | return comments; 15 | } 16 | 17 | beforeEach(startInjector); 18 | 19 | it('should query the server for a collection', function () { 20 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/posts\??/).respond(200, [p1, p2, p3, p4]); 21 | 22 | DS.findAll('post', {}).then(function (data) { 23 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 24 | }, function (err) { 25 | console.error(err.stack); 26 | fail('Should not have rejected!'); 27 | }); 28 | 29 | assert.deepEqual(DS.filter('post', {}), [], 'The posts should not be in the store yet'); 30 | 31 | // Should have no effect because there is already a pending query 32 | DS.findAll('post', {}).then(function (data) { 33 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 34 | }, function (err) { 35 | console.error(err.stack); 36 | fail('Should not have rejected!'); 37 | }); 38 | 39 | $httpBackend.flush(); 40 | 41 | assert.deepEqual(angular.toJson(DS.filter('post', {})), angular.toJson([p1, p2, p3, p4]), 'The posts are now in the store'); 42 | assert.isNumber(DS.lastModified('post', 5)); 43 | assert.isNumber(DS.lastSaved('post', 5)); 44 | DS.find('post', p1.id); // should not trigger another XHR 45 | 46 | 47 | // Should not make a request because the request was already completed 48 | DS.findAll('post', {}).then(function (data) { 49 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 50 | }, function (err) { 51 | console.error(err.stack); 52 | fail('Should not have rejected!'); 53 | }); 54 | 55 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/posts\??/).respond(200, [p1, p2, p3, p4]); 56 | 57 | // Should make a request because bypassCache is set to true 58 | DS.findAll('post', {}, { bypassCache: true }).then(function (data) { 59 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 60 | }, function (err) { 61 | console.error(err.stack); 62 | fail('Should not have rejected!'); 63 | }); 64 | 65 | $httpBackend.flush(); 66 | 67 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 68 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 69 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 70 | assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called'); 71 | }); 72 | it('should query the server for a collection but not store the data if cacheResponse is false', function () { 73 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/posts\??/).respond(200, [p1, p2, p3, p4]); 74 | 75 | DS.findAll('post', {}, { cacheResponse: false }).then(function (data) { 76 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 77 | }, function (err) { 78 | console.error(err.stack); 79 | fail('Should not have rejected!'); 80 | }); 81 | 82 | $httpBackend.flush(); 83 | 84 | assert.deepEqual(angular.toJson(DS.filter('post', {})), angular.toJson([]), 'The posts should not have been injected into the store'); 85 | 86 | assert.equal(lifecycle.beforeInject.callCount, 0, 'beforeInject should have been called'); 87 | assert.equal(lifecycle.afterInject.callCount, 0, 'afterInject should have been called'); 88 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 89 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 90 | }); 91 | it('should correctly propagate errors', function () { 92 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/posts\??/).respond(404, 'Not Found'); 93 | 94 | DS.findAll('post', {}).then(function () { 95 | fail('Should not have succeeded!'); 96 | }); 97 | 98 | try { 99 | $httpBackend.flush(); 100 | } catch (err) { 101 | assert.equal(err.data, 'Not Found'); 102 | } 103 | }); 104 | it('"params" argument is optional', function () { 105 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/posts\??/).respond(200, [p1, p2, p3, p4]); 106 | 107 | DS.findAll('post').then(function (data) { 108 | assert.deepEqual(angular.toJson(data), angular.toJson([p1, p2, p3, p4])); 109 | }, function (err) { 110 | console.error(err.message); 111 | fail('Should not have rejected!'); 112 | }); 113 | 114 | $httpBackend.flush(); 115 | 116 | assert.deepEqual(angular.toJson(DS.filter('post', {})), angular.toJson([p1, p2, p3, p4]), 'The posts are now in the store'); 117 | 118 | assert.equal(lifecycle.beforeInject.callCount, 1, 'beforeInject should have been called'); 119 | assert.equal(lifecycle.afterInject.callCount, 1, 'afterInject should have been called'); 120 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 121 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 122 | }); 123 | it('"params"', function () { 124 | $httpBackend.expectGET('http://test.angular-cache.com/posts?where=%7B%22author%22:%22Adam%22%7D').respond(200, [p4, p5]); 125 | 126 | var params = { 127 | where: { 128 | author: 'Adam' 129 | } 130 | }; 131 | DS.findAll('post', params).then(function (data) { 132 | assert.deepEqual(angular.toJson(data), angular.toJson([p4, p5])); 133 | }, function (err) { 134 | console.error(err.message); 135 | fail('Should not have rejected!'); 136 | }); 137 | 138 | $httpBackend.flush(); 139 | 140 | assert.deepEqual(angular.toJson(DS.filter('post', params)), angular.toJson([p4, p5]), 'The posts are now in the store'); 141 | assert.deepEqual(angular.toJson(DS.filter('post', { 142 | where: { 143 | id: { 144 | '>': 8 145 | } 146 | } 147 | })), angular.toJson([p5]), 'The posts are now in the store'); 148 | 149 | assert.equal(lifecycle.beforeInject.callCount, 1, 'beforeInject should have been called'); 150 | assert.equal(lifecycle.afterInject.callCount, 1, 'afterInject should have been called'); 151 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 152 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 153 | }); 154 | it('should return already injected items', function () { 155 | var u1 = { 156 | id: 1, 157 | name: 'John' 158 | }, 159 | u2 = { 160 | id: 2, 161 | name: 'Sally' 162 | }; 163 | 164 | DS.defineResource({ 165 | name: 'person', 166 | endpoint: 'users', 167 | methods: { 168 | fullName: function () { 169 | return this.first + ' ' + this.last; 170 | } 171 | } 172 | }); 173 | 174 | $httpBackend.expectGET(/http:\/\/test\.angular-cache\.com\/users\??/).respond(200, [u1, u2]); 175 | 176 | DS.findAll('person').then(function (data) { 177 | assert.deepEqual(angular.toJson(data), angular.toJson([ 178 | DSUtils.deepMixIn(new DS.definitions.person[DS.definitions.person.class](), u1), 179 | DSUtils.deepMixIn(new DS.definitions.person[DS.definitions.person.class](), u2) 180 | ])); 181 | angular.forEach(data, function (person) { 182 | assert.isTrue(person instanceof DS.definitions.person[DS.definitions.person.class], 'should be an instance of User'); 183 | }); 184 | }, function (err) { 185 | console.error(err.message); 186 | fail('Should not have rejected!'); 187 | }); 188 | 189 | $httpBackend.flush(); 190 | 191 | assert.deepEqual(angular.toJson(DS.filter('person')), angular.toJson([ 192 | DSUtils.deepMixIn(new DS.definitions.person[DS.definitions.person.class](), u1), 193 | DSUtils.deepMixIn(new DS.definitions.person[DS.definitions.person.class](), u2) 194 | ]), 'The users are now in the store'); 195 | 196 | assert.equal(lifecycle.beforeInject.callCount, 1, 'beforeInject should have been called'); 197 | assert.equal(lifecycle.afterInject.callCount, 1, 'afterInject should have been called'); 198 | assert.equal(lifecycle.serialize.callCount, 0, 'serialize should have been called'); 199 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 200 | }); 201 | it('should handle nested resources', function () { 202 | var testComment = { 203 | id: 5, 204 | content: 'test', 205 | approvedBy: 4 206 | }; 207 | var testComment2 = { 208 | id: 6, 209 | content: 'test', 210 | approvedBy: 4 211 | }; 212 | $httpBackend.expectGET('http://test.angular-cache.com/user/4/comment?content=test').respond(200, [testComment, testComment2]); 213 | 214 | DS.findAll('comment', { 215 | content: 'test' 216 | }, { 217 | params: { 218 | approvedBy: 4 219 | } 220 | }).then(function (comments) { 221 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 222 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 223 | content: 'test' 224 | }))); 225 | }, function () { 226 | fail('Should not have failed!'); 227 | }); 228 | 229 | $httpBackend.flush(); 230 | }); 231 | it('should handle nested resources 2', function () { 232 | var testComment = { 233 | id: 5, 234 | content: 'test', 235 | approvedBy: 4 236 | }; 237 | var testComment2 = { 238 | id: 6, 239 | content: 'test', 240 | approvedBy: 4 241 | }; 242 | 243 | $httpBackend.expectGET('http://test.angular-cache.com/comment?content=test').respond(200, [testComment, testComment2]); 244 | 245 | DS.findAll('comment', { 246 | content: 'test' 247 | }, { 248 | bypassCache: true 249 | }).then(function (comments) { 250 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 251 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 252 | content: 'test' 253 | }))); 254 | }, function () { 255 | fail('Should not have failed!'); 256 | }); 257 | 258 | $httpBackend.flush(); 259 | }); 260 | it('should handle nested resources 3', function () { 261 | var testComment = { 262 | id: 5, 263 | content: 'test', 264 | approvedBy: 4 265 | }; 266 | var testComment2 = { 267 | id: 6, 268 | content: 'test', 269 | approvedBy: 4 270 | }; 271 | 272 | DS.ejectAll('comment'); 273 | 274 | $httpBackend.expectGET('http://test.angular-cache.com/comment?content=test').respond(200, [testComment, testComment2]); 275 | 276 | DS.findAll('comment', { 277 | content: 'test' 278 | }, { 279 | bypassCache: true, 280 | params: { 281 | approvedBy: false 282 | } 283 | }).then(function (comments) { 284 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 285 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 286 | content: 'test' 287 | }))); 288 | }, function () { 289 | fail('Should not have failed!'); 290 | }); 291 | 292 | $httpBackend.flush(); 293 | }); 294 | it('stress test repeated injection', function () { 295 | this.timeout(15000); 296 | $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(100)); 297 | 298 | var start = new Date().getTime(); 299 | 300 | DS.findAll('comment', {}, { 301 | bypassCache: true 302 | }).then(function () { 303 | console.log('100 - time taken: ' + (new Date().getTime() - start) + 'ms'); 304 | }, function () { 305 | fail('Should not have failed!'); 306 | }); 307 | 308 | $httpBackend.flush(); 309 | 310 | $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(100)); 311 | 312 | start = new Date().getTime(); 313 | 314 | DS.findAll('comment', {}, { 315 | bypassCache: true 316 | }).then(function () { 317 | console.log('100 - time taken: ' + (new Date().getTime() - start) + 'ms'); 318 | }, function () { 319 | fail('Should not have failed!'); 320 | }); 321 | 322 | $httpBackend.flush(); 323 | 324 | $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(100)); 325 | 326 | start = new Date().getTime(); 327 | 328 | DS.findAll('comment', {}, { 329 | bypassCache: true 330 | }).then(function () { 331 | console.log('100 - time taken: ' + (new Date().getTime() - start) + 'ms'); 332 | }, function () { 333 | fail('Should not have failed!'); 334 | }); 335 | 336 | $httpBackend.flush(); 337 | }); 338 | //it('stress test 1000', function () { 339 | // this.timeout(15000); 340 | // $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(1000)); 341 | // 342 | // var start = new Date().getTime(); 343 | // 344 | // DS.findAll('comment', {}, { 345 | // bypassCache: true 346 | // }).then(function () { 347 | // console.log('1000 - time taken: ' + (new Date().getTime() - start) + 'ms'); 348 | // }, function () { 349 | // fail('Should not have failed!'); 350 | // }); 351 | // 352 | // $httpBackend.flush(); 353 | //}); 354 | //it('stress test 2000', function () { 355 | // this.timeout(30000); 356 | // $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(2000)); 357 | // 358 | // var start = new Date().getTime(); 359 | // 360 | // DS.findAll('comment', {}, { 361 | // bypassCache: true 362 | // }).then(function () { 363 | // console.log('2000 - time taken: ' + (new Date().getTime() - start) + 'ms'); 364 | // }, function () { 365 | // fail('Should not have failed!'); 366 | // }); 367 | // 368 | // $httpBackend.flush(); 369 | //}); 370 | //it('stress test 3000', function () { 371 | // this.timeout(30000); 372 | // $httpBackend.expectGET('http://test.angular-cache.com/comment').respond(200, createComments(3000)); 373 | // var start = new Date().getTime(); 374 | // 375 | // DS.findAll('comment', {}, { 376 | // bypassCache: true 377 | // }).then(function () { 378 | // console.log('3000 - time taken: ' + (new Date().getTime() - start) + 'ms'); 379 | // }, function () { 380 | // fail('Should not have failed!'); 381 | // }); 382 | // 383 | // $httpBackend.flush(); 384 | //}); 385 | }); 386 | -------------------------------------------------------------------------------- /test/datastore/async_methods/loadRelations.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.loadRelations', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should get an item from the server', function () { 5 | DS.inject('user', user10); 6 | 7 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user/10/comment').respond(200, [ 8 | comment11, 9 | comment12, 10 | comment13 11 | ]); 12 | $httpBackend.expectGET('http://test.angular-cache.com/profile?approvedBy=10&userId=10').respond(200, [profile15]); 13 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14?approvedBy=10').respond(200, organization14); 14 | 15 | DS.loadRelations('user', 10, ['comment', 'profile', 'organization'], { params: { approvedBy: 10 }, findStrictCache: true }).then(function (user) { 16 | try { 17 | assert.deepEqual(user.comments[0].id, DS.get('comment', user.comments[0].id).id); 18 | assert.deepEqual(user.comments[0].user, DS.get('comment', user.comments[0].id).user); 19 | assert.deepEqual(user.comments[1].id, DS.get('comment', user.comments[1].id).id); 20 | assert.deepEqual(user.comments[1].user, DS.get('comment', user.comments[1].id).user); 21 | assert.deepEqual(user.comments[2].id, DS.get('comment', user.comments[2].id).id); 22 | assert.deepEqual(user.comments[2].user, DS.get('comment', user.comments[2].id).user); 23 | assert.deepEqual(user.organization.id, DS.get('organization', 14).id); 24 | assert.deepEqual(user.profile.id, DS.get('profile', 15).id); 25 | // try a comment that has a belongsTo relationship to multiple users: 26 | DS.inject('comment', comment19); 27 | $httpBackend.expectGET('http://test.angular-cache.com/user/20').respond(200, user20); 28 | $httpBackend.expectGET('http://test.angular-cache.com/user/19').respond(200, user19); 29 | DS.loadRelations('comment', 19, ['user'], { findStrictCache: true }).then(function (comment) { 30 | try { 31 | assert.isObject(comment.user); 32 | assert.equal(comment.user.id, user20.id); 33 | assert.isObject(comment.approvedByUser); 34 | assert.equal(comment.approvedByUser.id, user19.id); 35 | } catch (err) { 36 | console.log(err, err.stack); 37 | fail(err); 38 | } 39 | }, function (err) { 40 | console.log(err, err.stack); 41 | fail(err); 42 | }); 43 | } catch (e) { 44 | console.log(e, e.stack); 45 | fail(e); 46 | } 47 | }, function (err) { 48 | console.log(err, err.stack); 49 | fail(err); 50 | }); 51 | 52 | setTimeout(function () { 53 | $httpBackend.flush(); 54 | }, 30); 55 | }); 56 | it('should get an item from the server but not store it if cacheResponse is false', function () { 57 | DS.inject('user', { 58 | name: 'John Anderson', 59 | id: 10, 60 | organizationId: 14 61 | }); 62 | 63 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user/10/comment').respond(200, [ 64 | comment11, 65 | comment12, 66 | comment13 67 | ]); 68 | $httpBackend.expectGET('http://test.angular-cache.com/profile?userId=10').respond(200, [profile15]); 69 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14').respond(200, organization14); 70 | 71 | DS.loadRelations('user', 10, ['comment', 'profile', 'organization'], { cacheResponse: false, findStrictCache: true }).then(function (user) { 72 | assert.deepEqual(angular.toJson(user.comments), angular.toJson([ 73 | comment11, 74 | comment12, 75 | comment13 76 | ])); 77 | assert.deepEqual(angular.toJson(user.organization), angular.toJson(organization14)); 78 | assert.deepEqual(angular.toJson(user.profile), angular.toJson(profile15)); 79 | 80 | assert.isUndefined(DS.get('comment', 11)); 81 | assert.isUndefined(DS.get('comment', 12)); 82 | assert.isUndefined(DS.get('comment', 13)); 83 | assert.isUndefined(DS.get('organization', 14)); 84 | assert.isUndefined(DS.get('profile', 15)); 85 | }).catch(function (e) { 86 | fail('should not have failed!'); 87 | }); 88 | 89 | setTimeout(function () { 90 | $httpBackend.flush(); 91 | }, 30); 92 | }); 93 | it('should correctly propagate errors', function () { 94 | DS.inject('user', { 95 | name: 'John Anderson', 96 | id: 10, 97 | organizationId: 14 98 | }); 99 | 100 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user/10/comment').respond(404, 'Not Found'); 101 | $httpBackend.expectGET('http://test.angular-cache.com/profile?userId=10').respond(404, 'Not Found'); 102 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14').respond(404, 'Not Found'); 103 | 104 | DS.loadRelations('user', 10, ['comment', 'profile', 'organization']).then(function () { 105 | fail('Should not have succeeded!'); 106 | }); 107 | 108 | try { 109 | $httpBackend.flush(); 110 | } catch (err) { 111 | assert.equal(err.data, 'Not Found'); 112 | } 113 | }); 114 | it('should handle multiple belongsTo levels', function () { 115 | var organization = DS.inject('organization', organization14); 116 | 117 | var copy = angular.extend({}, user10); 118 | delete copy.organization; 119 | delete copy.comments; 120 | delete copy.profile; 121 | 122 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user').respond(200, [copy]); 123 | 124 | DS.loadRelations('organization', organization, ['user'], { findStrictCache: true }).then(function (organization) { 125 | assert.equal(organization.users[0].id, 10); 126 | 127 | $httpBackend.expectGET('http://test.angular-cache.com/organization/14/user/10/comment').respond(200, [comment11, comment12]); 128 | 129 | var user = DS.get('user', 10); 130 | 131 | return DS.loadRelations('user', user, ['comment']).then(function (user) { 132 | assert.isArray(user.comments); 133 | }, function () { 134 | fail('Should not have succeeded!'); 135 | }); 136 | }, function (err) { 137 | console.log(err.stack); 138 | fail('Should not have succeeded!'); 139 | }); 140 | 141 | $httpBackend.flush(); 142 | }); 143 | it('should handle multiple belongsTo levels when the response includes nested resources', function () { 144 | var organization = DS.inject('organization', { 145 | id: 1 146 | }); 147 | 148 | $httpBackend.expectGET('http://test.angular-cache.com/organization/1/user').respond(200, [ 149 | { 150 | organizationId: 1, 151 | id: 1 152 | } 153 | ]); 154 | 155 | DS.loadRelations('organization', organization, ['user']).then(function (organization) { 156 | assert.equal(organization.users[0].id, 1); 157 | 158 | $httpBackend.expectGET('http://test.angular-cache.com/organization/1/user/1/comment').respond(200, [ 159 | { 160 | id: 1, 161 | userId: 1, 162 | user: { 163 | id: 1 164 | } 165 | }, 166 | { 167 | id: 2, 168 | userId: 1, 169 | user: { 170 | id: 1 171 | } 172 | } 173 | ]); 174 | 175 | var user = DS.get('user', 1); 176 | 177 | return DS.loadRelations('user', user, ['comment']).then(function (user) { 178 | assert.isArray(user.comments); 179 | }, function () { 180 | fail('Should not have succeeded!'); 181 | }); 182 | }, function () { 183 | fail('Should not have succeeded!'); 184 | }); 185 | 186 | $httpBackend.flush(); 187 | }); 188 | }); 189 | -------------------------------------------------------------------------------- /test/datastore/async_methods/refresh.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.refresh', function () { 2 | beforeEach(startInjector); 3 | it('should get an item from the server', function () { 4 | 5 | // Should do nothing because the data isn't in the store 6 | DS.refresh('post', 5).then(function (post) { 7 | assert.isUndefined(post); 8 | }); 9 | 10 | assert.isUndefined(DS.get('post', 5), 'The post should not be in the store yet'); 11 | 12 | DS.inject('post', p1); 13 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson(p1), 'The post is now in the store'); 14 | 15 | var initialLastModified = DS.lastModified('post', 5); 16 | 17 | $httpBackend.expectGET('http://test.angular-cache.com/posts/5').respond(200, { author: 'John', age: 31, id: 5 }); 18 | 19 | // Should refresh the item that's in the store 20 | DS.refresh('post', 5).then(function (post) { 21 | assert.deepEqual(angular.toJson(post), angular.toJson({ author: 'John', age: 31, id: 5 })); 22 | }, function (err) { 23 | console.error(err.stack); 24 | fail('Should not have rejected!'); 25 | }); 26 | 27 | // Should have no effect because the request is already pending 28 | DS.refresh('post', 5).then(function (post) { 29 | assert.deepEqual(angular.toJson(post), angular.toJson({ author: 'John', age: 31, id: 5 })); 30 | }, function (err) { 31 | console.error(err.stack); 32 | fail('Should not have rejected!'); 33 | }); 34 | 35 | $httpBackend.flush(); 36 | 37 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson({ 38 | author: 'John', 39 | age: 31, 40 | id: 5 41 | }), 'The post has been refreshed'); 42 | assert.notEqual(DS.lastModified('post', 5), initialLastModified); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/datastore/async_methods/save.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.save', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should save an item to the server and inject the result', function () { 5 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, { 6 | author: 'Jake', 7 | id: 5, 8 | age: 30 9 | }); 10 | 11 | DS.inject('post', p1); 12 | 13 | var initialModified = DS.lastModified('post', 5); 14 | var initialSaved = DS.lastSaved('post', 5); 15 | 16 | DS.get('post', 5).author = 'Jake'; 17 | 18 | DS.save('post', 5).then(function (post) { 19 | assert.deepEqual(post, DS.get('post', 5), 'post 5 should have been saved'); 20 | assert.equal(post.author, 'Jake'); 21 | }, function (err) { 22 | console.error(err.stack); 23 | fail('should not have rejected'); 24 | }); 25 | 26 | $httpBackend.flush(); 27 | 28 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 29 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 30 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson({ 31 | author: 'Jake', 32 | age: 30, 33 | id: 5 34 | })); 35 | DS.digest(); 36 | assert.notEqual(DS.lastModified('post', 5), initialModified); 37 | assert.notEqual(DS.lastSaved('post', 5), initialSaved); 38 | 39 | DS.save('post', 6).then(function () { 40 | fail('should not have succeeded'); 41 | }, function (err) { 42 | assert.isTrue(err instanceof DS.errors.RuntimeError); 43 | assert.equal(err.message, 'id "6" not found in cache!'); 44 | }); 45 | 46 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 47 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 48 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 49 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 50 | }); 51 | it('should save an item to the server and inject the result when using a custom http method', function () { 52 | $httpBackend.expectPATCH('http://test.angular-cache.com/posts/5').respond(200, { 53 | author: 'Jake', 54 | id: 5, 55 | age: 30 56 | }); 57 | 58 | DS.inject('post', p1); 59 | 60 | DS.get('post', 5).author = 'Jake'; 61 | 62 | DS.save('post', 5, { method: 'PATCH' }).then(function (post) { 63 | assert.deepEqual(post, DS.get('post', 5), 'post 5 should have been saved'); 64 | assert.equal(post.author, 'Jake'); 65 | }, function (err) { 66 | console.error(err.stack); 67 | fail('should not have reached this'); 68 | }); 69 | 70 | $httpBackend.flush(); 71 | }); 72 | it('should save an item to the server and inject the result via the instance method', function () { 73 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, { 74 | author: 'Jake', 75 | id: 5, 76 | age: 30 77 | }); 78 | 79 | DS.inject('post', p1); 80 | 81 | var initialModified = DS.lastModified('post', 5); 82 | var initialSaved = DS.lastSaved('post', 5); 83 | 84 | DS.get('post', 5).author = 'Jake'; 85 | 86 | DS.get('post', 5).DSSave().then(function (post) { 87 | assert.deepEqual(post, DS.get('post', 5), 'post 5 should have been saved'); 88 | assert.equal(post.author, 'Jake'); 89 | }, function (err) { 90 | console.error(err.stack); 91 | fail('should not have rejected'); 92 | }); 93 | 94 | $httpBackend.flush(); 95 | 96 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 97 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 98 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson({ 99 | author: 'Jake', 100 | age: 30, 101 | id: 5 102 | })); 103 | DS.digest(); 104 | assert.notEqual(DS.lastModified('post', 5), initialModified); 105 | assert.notEqual(DS.lastSaved('post', 5), initialSaved); 106 | }); 107 | it('should save an item to the server but not inject the result', function () { 108 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, { 109 | random: 'stuff' 110 | }); 111 | 112 | DS.inject('post', p1); 113 | 114 | var initialSaved = DS.lastSaved('post', 5); 115 | 116 | DS.get('post', 5).author = 'Jake'; 117 | 118 | $rootScope.$apply(); 119 | 120 | DS.save('post', 5, { cacheResponse: false }).then(function (post) { 121 | assert.deepEqual(DS.utils.toJson(post), DS.utils.toJson({ 122 | random: 'stuff' 123 | }), 'should have the right response'); 124 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 125 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 126 | assert.equal(lifecycle.beforeInject.callCount, 1, 'beforeInject should have been called only once'); 127 | assert.equal(lifecycle.afterInject.callCount, 1, 'afterInject should have been called only once'); 128 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson({ 129 | author: 'Jake', 130 | age: 30, 131 | id: 5 132 | })); 133 | assert.equal(DS.lastSaved('post', 5), initialSaved); 134 | }, function (err) { 135 | console.error(err.stack); 136 | fail('should not have rejected'); 137 | }); 138 | 139 | $httpBackend.flush(); 140 | }); 141 | it('should save changes of an item to the server', function () { 142 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5', { author: 'Jake' }).respond(200, { 143 | author: 'Jake', 144 | id: 5, 145 | age: 30 146 | }); 147 | 148 | DS.inject('post', p1); 149 | 150 | var initialModified = DS.lastModified('post', 5); 151 | var initialSaved = DS.lastSaved('post', 5); 152 | var post1 = DS.get('post', 5); 153 | 154 | post1.author = 'Jake'; 155 | 156 | DS.digest(); 157 | 158 | DS.save('post', 5, { changesOnly: true }).then(function (post) { 159 | assert.deepEqual(angular.toJson(post), angular.toJson(post1), 'post 5 should have been saved'); 160 | assert.equal(post.author, 'Jake'); 161 | }, function (err) { 162 | console.error(err.stack); 163 | fail('should not have rejected'); 164 | }); 165 | 166 | $httpBackend.flush(); 167 | 168 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 169 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 170 | assert.deepEqual(angular.toJson(DS.get('post', 5)), angular.toJson(post1)); 171 | assert.notEqual(DS.lastModified('post', 5), initialModified); 172 | assert.notEqual(DS.lastSaved('post', 5), initialSaved); 173 | 174 | DS.save('post', 6).then(function () { 175 | fail('should not have succeeded'); 176 | }, function (err) { 177 | assert.isTrue(err instanceof DS.errors.RuntimeError); 178 | assert.equal(err.message, 'id "6" not found in cache!'); 179 | }); 180 | 181 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 182 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 183 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 184 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 185 | }); 186 | }); 187 | -------------------------------------------------------------------------------- /test/datastore/async_methods/update.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.update', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should update an item', function () { 5 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, { author: 'Jake', age: 30, id: 5 }); 6 | 7 | var post = DS.inject('post', p1); 8 | 9 | var initialModified = DS.lastModified('post', 5); 10 | var initialSaved = DS.lastSaved('post', 5); 11 | 12 | DS.update('post', 5, { author: 'Jake' }).then(function (p) { 13 | assert.deepEqual(p, post, 'post 5 should have been updated'); 14 | assert.equal(p.author, 'Jake'); 15 | assert.equal(post.author, 'Jake'); 16 | }, function (err) { 17 | console.error(err.stack); 18 | fail('should not have rejected'); 19 | }); 20 | 21 | $httpBackend.flush(); 22 | 23 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/6').respond(200, { author: 'Jane', age: 31, id: 6 }); 24 | 25 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 26 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 27 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 28 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 29 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 30 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 31 | assert.deepEqual(DS.get('post', 5), post); 32 | assert.notEqual(DS.lastModified('post', 5), initialModified); 33 | assert.notEqual(DS.lastSaved('post', 5), initialSaved); 34 | 35 | DS.update('post', 6, { author: 'Jane' }).then(function (p) { 36 | assert.deepEqual(angular.toJson(p), angular.toJson(DS.get('post', 6))); 37 | assert.deepEqual(angular.toJson(p), angular.toJson({ author: 'Jane', age: 31, id: 6 })); 38 | }, function (err) { 39 | console.error(err.stack); 40 | fail('should not have rejected'); 41 | }); 42 | 43 | $httpBackend.flush(); 44 | 45 | assert.equal(lifecycle.beforeInject.callCount, 3, 'beforeInject should have been called'); 46 | assert.equal(lifecycle.afterInject.callCount, 3, 'afterInject should have been called'); 47 | assert.equal(lifecycle.serialize.callCount, 2, 'serialize should have been called'); 48 | assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called'); 49 | }); 50 | it('should update an item via the instance method', function () { 51 | $httpBackend.expectPUT('http://test.angular-cache.com/posts/5').respond(200, { author: 'Jake', age: 30, id: 5 }); 52 | 53 | var post = DS.inject('post', p1); 54 | 55 | var initialModified = DS.lastModified('post', 5); 56 | var initialSaved = DS.lastSaved('post', 5); 57 | 58 | post.DSUpdate({ author: 'Jake' }).then(function (p) { 59 | assert.deepEqual(angular.toJson(p), angular.toJson(post), 'post 5 should have been updated'); 60 | assert.equal(p.author, 'Jake'); 61 | assert.equal(post.author, 'Jake'); 62 | }, function (err) { 63 | console.error(err.stack); 64 | fail('should not have rejected'); 65 | }); 66 | 67 | $httpBackend.flush(); 68 | 69 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 70 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 71 | assert.equal(lifecycle.beforeInject.callCount, 2, 'beforeInject should have been called'); 72 | assert.equal(lifecycle.afterInject.callCount, 2, 'afterInject should have been called'); 73 | assert.equal(lifecycle.serialize.callCount, 1, 'serialize should have been called'); 74 | assert.equal(lifecycle.deserialize.callCount, 1, 'deserialize should have been called'); 75 | assert.deepEqual(DS.get('post', 5), post); 76 | assert.notEqual(DS.lastModified('post', 5), initialModified); 77 | assert.notEqual(DS.lastSaved('post', 5), initialSaved); 78 | }); 79 | it('should handle nested resources', function () { 80 | var testComment = { 81 | id: 5, 82 | content: 'stuff', 83 | approvedBy: 4 84 | }; 85 | var testComment2 = { 86 | id: 6, 87 | content: 'stuff', 88 | approvedBy: 4 89 | }; 90 | $httpBackend.expectPUT('http://test.angular-cache.com/user/4/comment/5').respond(200, testComment); 91 | 92 | DS.inject('comment', testComment); 93 | 94 | DS.update('comment', 5, { 95 | content: 'stuff' 96 | }).then(function (comment) { 97 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment)); 98 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 5))); 99 | }, function () { 100 | fail('Should not have failed!'); 101 | }); 102 | 103 | $httpBackend.flush(); 104 | 105 | $httpBackend.expectPUT('http://test.angular-cache.com/user/4/comment/6', { content: 'stuff', other: 'stuff' }).respond(200, testComment2); 106 | 107 | var comment = DS.inject('comment', testComment2); 108 | 109 | function onBeforeUpdate(resource, attrs) { 110 | try { 111 | attrs.other = 'stuff'; 112 | assert.equal(angular.toJson(attrs), angular.toJson({ content: 'stuff', other: 'stuff' })); 113 | } catch (e) { 114 | console.log(e.stack); 115 | } 116 | } 117 | 118 | function onAfterUpdate(resource, attrs) { 119 | try { 120 | assert.deepEqual(angular.toJson(attrs), angular.toJson(testComment2)); 121 | assert.isFalse(testComment2 === attrs); 122 | } catch (e) { 123 | console.log(e.stack); 124 | } 125 | } 126 | 127 | Comment.on('DS.beforeUpdate', onBeforeUpdate); 128 | Comment.on('DS.afterUpdate', onAfterUpdate); 129 | 130 | Comment.update(comment, { 131 | content: 'stuff' 132 | }, { 133 | params: { 134 | approvedBy: 4 135 | } 136 | }).then(function (comment) { 137 | try { 138 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment2)); 139 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 6))); 140 | } catch (e) { 141 | console.log(e.stack); 142 | } 143 | }, function () { 144 | fail('Should not have failed!'); 145 | }); 146 | 147 | $httpBackend.flush(); 148 | 149 | $httpBackend.expectPUT('http://test.angular-cache.com/comment/6').respond(200, testComment2); 150 | 151 | DS.inject('comment', testComment2); 152 | 153 | DS.update('comment', 6, { 154 | content: 'stuff' 155 | }, { 156 | params: { 157 | approvedBy: false 158 | } 159 | }).then(function (comment) { 160 | try { 161 | assert.deepEqual(angular.toJson(comment), angular.toJson(testComment2)); 162 | assert.deepEqual(angular.toJson(comment), angular.toJson(DS.get('comment', 6))); 163 | } catch (e) { 164 | console.log(e.stack); 165 | } 166 | }, function () { 167 | fail('Should not have failed!'); 168 | }); 169 | 170 | $httpBackend.flush(); 171 | }); 172 | }); 173 | -------------------------------------------------------------------------------- /test/datastore/async_methods/updateAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.updateAll', function () { 2 | beforeEach(startInjector); 3 | 4 | it('should update a collection of items', function () { 5 | $httpBackend.expectPUT('http://test.angular-cache.com/posts?where=%7B%22age%22:%7B%22%3D%3D%22:33%7D%7D').respond(200, [ 6 | { author: 'Adam', age: 27, id: 8 }, 7 | { author: 'Adam', age: 27, id: 9 } 8 | ]); 9 | 10 | var post4 = DS.inject('post', p4); 11 | var post5 = DS.inject('post', p5); 12 | var posts = DS.filter('post', { where: { age: { '==': 33 } } }); 13 | 14 | var initialModified = DS.lastModified('post', 8); 15 | var initialSaved = DS.lastSaved('post', 8); 16 | 17 | DS.updateAll('post', { age: 27 }, { where: { age: { '==': 33 } } }).then(function (ps) { 18 | assert.deepEqual(angular.toJson(ps), angular.toJson(posts), '2 posts should have been updated'); 19 | assert.equal(posts[0].age, 27); 20 | assert.equal(posts[1].age, 27); 21 | assert.equal(post4.age, 27); 22 | assert.equal(post5.age, 27); 23 | }, function (err) { 24 | console.error(err.stack); 25 | fail('should not have rejected'); 26 | }); 27 | 28 | $httpBackend.flush(); 29 | 30 | $httpBackend.expectPUT('http://test.angular-cache.com/posts?where=%7B%22age%22:%7B%22%3D%3D%22:31%7D%7D').respond(200, [ 31 | { author: 'Jane', age: 5, id: 6 } 32 | ]); 33 | 34 | assert.equal(lifecycle.beforeUpdate.callCount, 1, 'beforeUpdate should have been called'); 35 | assert.equal(lifecycle.afterUpdate.callCount, 1, 'afterUpdate should have been called'); 36 | assert.deepEqual(angular.toJson(DS.filter('post', { where: { age: { '==': 27 } } })), angular.toJson(posts)); 37 | assert.notEqual(DS.lastModified('post', 8), initialModified); 38 | assert.notEqual(DS.lastSaved('post', 8), initialSaved); 39 | 40 | DS.updateAll('post', { age: 5 }, { where: { age: { '==': 31 } } }).then(function (ps) { 41 | assert.deepEqual(angular.toJson(ps), angular.toJson(DS.filter('post', { where: { age: { '==': 5 } } }))); 42 | assert.deepEqual(angular.toJson(ps[0]), angular.toJson({ author: 'Jane', age: 5, id: 6 })); 43 | }, function (err) { 44 | console.error(err.stack); 45 | fail('should not have rejected'); 46 | }); 47 | 48 | $httpBackend.flush(); 49 | 50 | assert.equal(lifecycle.beforeInject.callCount, 4, 'beforeInject should have been called'); 51 | assert.equal(lifecycle.afterInject.callCount, 4, 'afterInject should have been called'); 52 | assert.equal(lifecycle.serialize.callCount, 2, 'serialize should have been called'); 53 | assert.equal(lifecycle.deserialize.callCount, 2, 'deserialize should have been called'); 54 | }); 55 | it('should handle nested resources', function () { 56 | var testComment = { 57 | id: 5, 58 | content: 'stuff', 59 | approvedBy: 4 60 | }; 61 | var testComment2 = { 62 | id: 6, 63 | content: 'stuff', 64 | approvedBy: 4 65 | }; 66 | $httpBackend.expectPUT('http://test.angular-cache.com/user/4/comment?content=test').respond(200, [testComment, testComment2]); 67 | 68 | DS.inject('comment', testComment); 69 | 70 | DS.updateAll('comment', { 71 | content: 'stuff' 72 | }, { 73 | content: 'test' 74 | }, { 75 | params: { 76 | approvedBy: 4 77 | } 78 | }).then(function (comments) { 79 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 80 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 81 | content: 'stuff' 82 | }))); 83 | }, function () { 84 | fail('Should not have failed!'); 85 | }); 86 | 87 | $httpBackend.flush(); 88 | }); 89 | it('should handle nested resources 2', function () { 90 | var testComment = { 91 | id: 5, 92 | content: 'stuff', 93 | approvedBy: 4 94 | }; 95 | var testComment2 = { 96 | id: 6, 97 | content: 'stuff', 98 | approvedBy: 4 99 | }; 100 | 101 | $httpBackend.expectPUT('http://test.angular-cache.com/comment?content=test').respond(200, [testComment, testComment2]); 102 | 103 | DS.inject('comment', testComment2); 104 | 105 | DS.updateAll('comment', { 106 | content: 'stuff' 107 | }, { 108 | content: 'test' 109 | }).then(function (comments) { 110 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 111 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 112 | content: 'stuff', 113 | sort: 'id' 114 | }))); 115 | }, function () { 116 | fail('Should not have failed!'); 117 | }); 118 | 119 | $httpBackend.flush(); 120 | }); 121 | it('should handle nested resources 3', function () { 122 | var testComment = { 123 | id: 5, 124 | content: 'stuff', 125 | approvedBy: 4 126 | }; 127 | var testComment2 = { 128 | id: 6, 129 | content: 'stuff', 130 | approvedBy: 4 131 | }; 132 | 133 | $httpBackend.expectPUT('http://test.angular-cache.com/comment?content=test').respond(200, [testComment, testComment2]); 134 | 135 | DS.inject('comment', testComment2); 136 | 137 | DS.updateAll('comment', { 138 | content: 'stuff' 139 | }, { 140 | content: 'test' 141 | }, { 142 | params: { 143 | approvedBy: false 144 | } 145 | }).then(function (comments) { 146 | assert.deepEqual(angular.toJson(comments), angular.toJson([testComment, testComment2])); 147 | assert.deepEqual(angular.toJson(comments), angular.toJson(DS.filter('comment', { 148 | content: 'stuff', 149 | sort: 'id' 150 | }))); 151 | }, function () { 152 | fail('Should not have failed!'); 153 | }); 154 | $httpBackend.flush(); 155 | }); 156 | }); 157 | -------------------------------------------------------------------------------- /test/datastore/sync_methods/bindAll.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.bindAll', function () { 2 | var $rootScope, $scope; 3 | 4 | beforeEach(startInjector); 5 | 6 | beforeEach(function () { 7 | inject(function (_$rootScope_) { 8 | $rootScope = _$rootScope_; 9 | $scope = $rootScope.$new(); 10 | }); 11 | }); 12 | 13 | it('should throw an error when method pre-conditions are not met', function () { 14 | assert.throws(function () { 15 | DS.bindAll('does not exist'); 16 | }, Error, 'does not exist is not a registered resource!'); 17 | 18 | angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { 19 | if (key) { 20 | assert.throws(function () { 21 | DS.bindAll('post', key); 22 | }, Error, '"params" must be an object!'); 23 | } 24 | }); 25 | 26 | angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { 27 | assert.throws(function () { 28 | DS.bindAll('post', {}, key); 29 | }, Error, '"scope" must be an object!'); 30 | }); 31 | 32 | angular.forEach(TYPES_EXCEPT_STRING, function (key) { 33 | assert.throws(function () { 34 | DS.bindAll('post', {}, $scope, key); 35 | }, Error, '"expr" must be a string!'); 36 | }); 37 | }); 38 | it('should bind an item in the data store to the scope', function () { 39 | 40 | DS.inject('post', p1); 41 | DS.inject('post', p2); 42 | DS.inject('post', p3); 43 | DS.inject('post', p4); 44 | DS.inject('post', p5); 45 | 46 | DS.bindAll('post', { 47 | where: { 48 | age: { 49 | '>': 31 50 | } 51 | } 52 | }, $scope, 'posts'); 53 | 54 | $rootScope.$apply(); 55 | 56 | assert.deepEqual(angular.toJson($scope.posts), angular.toJson([p3, p4, p5])); 57 | 58 | DS.eject('post', 8); 59 | 60 | $rootScope.$apply(); 61 | 62 | assert.deepEqual(angular.toJson($scope.posts), angular.toJson([p3, p5])); 63 | }); 64 | it('should execute a callback if given', function () { 65 | 66 | var cb = sinon.spy(); 67 | DS.inject('post', p1); 68 | DS.inject('post', p2); 69 | DS.inject('post', p3); 70 | DS.inject('post', p4); 71 | DS.inject('post', p5); 72 | 73 | DS.bindAll('post', { 74 | where: { 75 | age: { 76 | '>': 31 77 | } 78 | } 79 | }, $scope, 'posts', cb); 80 | 81 | $rootScope.$apply(); 82 | 83 | assert.deepEqual(angular.toJson($scope.posts), angular.toJson([p3, p4, p5])); 84 | assert.equal(cb.callCount, 1); 85 | 86 | DS.eject('post', 8); 87 | 88 | $rootScope.$apply(function () { 89 | assert.deepEqual(angular.toJson($scope.posts), angular.toJson([p3, p5])); 90 | assert.equal(cb.callCount, 2); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/datastore/sync_methods/bindOne.test.js: -------------------------------------------------------------------------------- 1 | describe('DS.bindOne', function () { 2 | var $rootScope, $scope; 3 | 4 | beforeEach(startInjector); 5 | 6 | beforeEach(function () { 7 | inject(function (_$rootScope_) { 8 | $rootScope = _$rootScope_; 9 | $scope = $rootScope.$new(); 10 | }); 11 | }); 12 | 13 | it('should throw an error when method pre-conditions are not met', function () { 14 | assert.throws(function () { 15 | DS.bindOne('does not exist'); 16 | }, Error, 'does not exist is not a registered resource!'); 17 | 18 | angular.forEach(TYPES_EXCEPT_STRING_OR_NUMBER, function (key) { 19 | assert.throws(function () { 20 | DS.bindOne('post', key); 21 | }, Error, '"id" must be a string or a number!'); 22 | }); 23 | 24 | angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { 25 | assert.throws(function () { 26 | DS.bindOne('post', 5, key); 27 | }, Error, '"scope" must be an object!'); 28 | }); 29 | 30 | angular.forEach(TYPES_EXCEPT_STRING, function (key) { 31 | assert.throws(function () { 32 | DS.bindOne('post', 5, $scope, key); 33 | }, Error, '"expr" must be a string!'); 34 | }); 35 | }); 36 | it('should bind an item in the data store to the scope', function () { 37 | 38 | DS.inject('post', p1); 39 | DS.inject('post', p2); 40 | 41 | var post = DS.get('post', 5); 42 | var post2 = DS.get('post', 6); 43 | 44 | DS.bindOne('post', 5, $scope, 'post'); 45 | DS.bindOne('post', 6, $scope, 'other.post'); 46 | DS.bindOne('post', 7, $scope, 'post3'); 47 | 48 | $rootScope.$apply(); 49 | 50 | assert.deepEqual($scope.post, post); 51 | assert.deepEqual($scope.other.post, post2); 52 | assert.isUndefined($scope.post2); 53 | 54 | post.author = 'Jason'; 55 | 56 | $rootScope.$apply(); 57 | 58 | assert.equal($scope.post.author, 'Jason'); 59 | assert.deepEqual($scope.post, post); 60 | assert.deepEqual($scope.other.post, post2); 61 | assert.isUndefined($scope.post2); 62 | }); 63 | it('should execute a callback if given', function (done) { 64 | 65 | var Post = DS.definitions.post; 66 | var cb = sinon.spy(); 67 | Post.inject(p1); 68 | 69 | var post = Post.get(5); 70 | 71 | Post.bindOne(5, $scope, 'post', cb); 72 | 73 | $rootScope.$apply(); 74 | 75 | assert.equal(cb.callCount, 1); 76 | assert.deepEqual($scope.post, post); 77 | 78 | post.author = 'Jason'; 79 | 80 | DS.digest(); 81 | $rootScope.$apply(); 82 | 83 | setTimeout(function () { 84 | $rootScope.$apply(); 85 | 86 | assert.equal(cb.callCount, 2); 87 | assert.equal($scope.post.author, 'Jason'); 88 | assert.deepEqual($scope.post, post); 89 | 90 | done(); 91 | }, 50); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | module.exports = { 4 | devtool: 'source-map', 5 | entry: { 6 | './dist/js-data-angular.js': './src/index.js' 7 | }, 8 | output: { 9 | filename: '[name]', 10 | libraryTarget: 'umd2', 11 | library: 'jsDataAngularModuleName' 12 | }, 13 | externals: { 14 | 'js-data': { 15 | amd: 'js-data', 16 | commonjs: 'js-data', 17 | commonjs2: 'js-data', 18 | root: 'JSData' 19 | }, 20 | 'axios': 'axios', 21 | 'angular': 'angular' 22 | }, 23 | module: { 24 | loaders: [ 25 | { 26 | loader: 'babel-loader', 27 | include: [ 28 | path.resolve(__dirname, 'src'), 29 | path.resolve(__dirname) 30 | ], 31 | test: /\.js$/ 32 | } 33 | ] 34 | } 35 | } 36 | --------------------------------------------------------------------------------